如何读取sqlx中的外键

How to read foreign keys in sqlx

在我的数据库中有几个表:

团队

| Field | Type    |
| ----- | ------- |
| ID    | int     |
| Name  | varchar |

夹具

| Field | Type |
| ----- | ---- |
| ID    | int  |
| Home  | int  |
| Away  | int  |

很明显,HomeAway 列是 Team 的外键。

在 Go 中,我将这些定义为以下类型:

type Team struct {
  ID   int64  `json:"id" db:"id"`
  Name string `json:"name" db:"name"`
}

type Fixture struct {
  ID   int64 `json:"id" db:"id"`
  Home int64 `json:"home" db:"home"`
  Away int64 `json:"away" db:"away"`
}

我希望能够从数据库中读取一段固定装置,例如:

var fs []Fixture
err := sqlx.DB.SelectContext(ctx, &fs, `SELECT * FROM fixture`)

但这会导致错误:

Scan error on column index 2, name "home": unsupported Scan, storing driver.Value type int64 into type *teams.Team

我在此处阅读过有关嵌入的内容,但我看不出这有什么帮助,因为 HomeAway 属于同一类型,因此会出现命名冲突。

非常感谢任何帮助

首先,没有办法直接,例如在 struct Y 上声明一些 X 类型的字段并让数据库驱动程序自动反序列化它们,因为两端的数据模型根本不同:数据库是关系型的(假设这是因为你正在使用sqlx) 而 Go 是 面向对象 结构的。所以这就是 ORM(对象关系映射,因此得名)的工作。但是假设您还不想使用 ORM。

其次,无论现有的 FK 约束如何,您正在使用 return 的 SQL 查询仅包含 fixture table 中的数据。它不会神奇地 return 其他数据。所以你要么查询数据库两次,要么你必须 JOIN 两个 tables:

SELECT 
  f.id, 
  t1.id as home_team_id, 
  t1.name as home_team_name,
  t2.id as away_team_id,
  t2.name as away_team_name
FROM
  fixture f
  JOIN team t1 ON t1.id = f.home
  JOIN team t2 ON t2.id = f.away

请注意带有 as 的别名,它们有助于区分列名。这 returns 是必要的数据字段。现在你可以使用第三个桥结构来正确扫描从数据库中获取的行,并用它初始化你的主要结构:

    // Here I'm using (a slice of) anonymous structs, 
    // but you can declare a named one instead.
    bridge := []struct {
        FixtureId    int64  `db:"id"`
        HomeTeamId   int64  `db:"home_team_id"`
        HomeTeamName string `db:"home_team_name"`
        AwayTeamId   int64  `db:"away_team_id"`
        AwayTeamName string `db:"away_team_name"`
    }{}
    err := db.SelectContext(ctx, &bridge, query)
    if err != nil {
        // handle err...
    }
    mystructs := make([]Fixture, len(bridge))
    for i, b := range bridge {
        mystructs[i] = Fixture{
            ID: b.FixtureId,
            Home: Team{
                ID:   b.HomeTeamId,
                Name: b.HomeTeamName,
            },
            Away: Team{
                ID:   b.AwayTeamId,
                Name: b.AwayTeamName,
            },
        }
    }

其他不太方便的替代方法是:

  • 使用 sqlx.Queryx(ctx, query) 得到 *sqlx.Rows 结果并遍历它,初始化你的 Fixture 结构。 (完成后记得关闭Rows,释放资源)
  • 让父结构 Fixture 实现 sql.Scanner (an example)