Golang如何将联表查询结果解析到嵌套结构体中

Golang如何将联表查询结果解析到嵌套结构体中 编辑: 看来PGX不太适合这个场景。它是一个底层驱动,意味着我必须手动扫描结果。有谁能对下面的问题提出更好的方法吗?

目前我正在使用pgx,但如果它无法实现,我也愿意更换。

有没有一种简单的方法可以从查询中解析子结构的值?

我执行了一个连接查询,如下所示:

rows, err := conn.Query(context.Background(),
    `SELECT 
        rdr.id AS r_Id,
        rdr.field1 AS r_Field2,
        rdr.field2 AS r_Field2,
        contact.firstname AS c_FirstName,
        contact.lastname AS c_LastName,
        contact.id AS c_Id 
    FROM rdr
    INNER JOIN contact ON rdr.contact=contact.id`)

我的结构体定义如下:

type Rdr struct {
    Id              int32     
    Field1          int32     
    Contact         Contact   
    Field2          time.Time
}

type Contact struct {
    Id        int
    FirstName string
    LastName  string
}

如何将查询返回的行数据解析到我的 Rdr 结构体中?我需要将正确的字段解析到 Contact 中,理想情况下是同时进行,但我不知道如何实现。我不明白 Scan 方法如何能做到这一点。我们查看了 FieldDescriptionsValues,但这变得非常混乱。有没有一种简单或通用的方法来解决这个问题?我想这是一个非常常见的问题。


更多关于Golang如何将联表查询结果解析到嵌套结构体中的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

如果将 Contact Contact 改为嵌入式结构体字段(即仅使用 Contact),那么 https://github.com/jmoiron/sqlx 应该可以正常工作。

更多关于Golang如何将联表查询结果解析到嵌套结构体中的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go中处理嵌套结构体的SQL结果映射,可以使用以下两种主要方法:

方法1:使用pgx的Rows.Scan()手动映射

rows, err := conn.Query(context.Background(),
    `SELECT 
        rdr.id AS r_Id,
        rdr.field1 AS r_Field1,
        rdr.field2 AS r_Field2,
        contact.firstname AS c_FirstName,
        contact.lastname AS c_LastName,
        contact.id AS c_Id 
    FROM rdr
    INNER JOIN contact ON rdr.contact=contact.id`)

var results []Rdr
for rows.Next() {
    var r Rdr
    var c Contact
    
    err := rows.Scan(
        &r.Id,
        &r.Field1,
        &r.Field2,
        &c.FirstName,
        &c.LastName,
        &c.Id,
    )
    if err != nil {
        return nil, err
    }
    
    r.Contact = c
    results = append(results, r)
}

方法2:使用sqlx库(推荐)

sqlx提供了更简洁的嵌套结构体映射:

import "github.com/jmoiron/sqlx"

type Rdr struct {
    Id      int32     `db:"r_id"`
    Field1  int32     `db:"r_field1"`
    Field2  time.Time `db:"r_field2"`
    Contact Contact   `db:""` // 嵌套结构体
}

type Contact struct {
    Id        int    `db:"c_id"`
    FirstName string `db:"c_firstname"`
    LastName  string `db:"c_lastname"`
}

// 使用sqlx查询
var results []Rdr
err := db.Select(&results,
    `SELECT 
        rdr.id AS r_id,
        rdr.field1 AS r_field1,
        rdr.field2 AS r_field2,
        contact.firstname AS c_firstname,
        contact.lastname AS c_lastname,
        contact.id AS c_id 
    FROM rdr
    INNER JOIN contact ON rdr.contact=contact.id`)

方法3:使用pgx的RowToStructByName(pgx/v5)

pgx v5提供了更好的结构体映射支持:

import "github.com/jackc/pgx/v5"

rows, _ := conn.Query(ctx, query)
results, err := pgx.CollectRows(rows, pgx.RowToStructByName[Rdr])

// 或者使用自定义映射
rows, _ := conn.Query(ctx, query)
results, err := pgx.CollectRows(rows, func(row pgx.CollectableRow) (Rdr, error) {
    var r Rdr
    var c Contact
    
    err := row.Scan(
        &r.Id,
        &r.Field1,
        &r.Field2,
        &c.FirstName,
        &c.LastName,
        &c.Id,
    )
    r.Contact = c
    return r, err
})

方法4:使用结构体标签和反射自定义映射

如果需要更灵活的控制,可以创建自定义扫描函数:

func scanRdrWithContact(row pgx.Row, r *Rdr) error {
    return row.Scan(
        &r.Id,
        &r.Field1,
        &r.Field2,
        &r.Contact.FirstName,
        &r.Contact.LastName,
        &r.Contact.Id,
    )
}

// 使用示例
rows, _ := conn.Query(ctx, query)
for rows.Next() {
    var r Rdr
    if err := scanRdrWithContact(rows, &r); err != nil {
        return err
    }
    results = append(results, r)
}

对于联表查询嵌套结构体的场景,推荐使用sqlx库,它提供了最简洁的语法和良好的性能。如果必须使用pgx,pgx/v5的CollectRows配合RowToStructByName是不错的选择。

回到顶部