如何读取sqlx中的外键
How to read foreign keys in sqlx
在我的数据库中有几个表:
团队
| Field | Type |
| ----- | ------- |
| ID | int |
| Name | varchar |
夹具
| Field | Type |
| ----- | ---- |
| ID | int |
| Home | int |
| Away | int |
很明显,Home
和 Away
列是 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
我在此处阅读过有关嵌入的内容,但我看不出这有什么帮助,因为 Home
和 Away
属于同一类型,因此会出现命名冲突。
非常感谢任何帮助
首先,没有办法直接,例如在 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)
在我的数据库中有几个表:
团队
| Field | Type |
| ----- | ------- |
| ID | int |
| Name | varchar |
夹具
| Field | Type |
| ----- | ---- |
| ID | int |
| Home | int |
| Away | int |
很明显,Home
和 Away
列是 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
我在此处阅读过有关嵌入的内容,但我看不出这有什么帮助,因为 Home
和 Away
属于同一类型,因此会出现命名冲突。
非常感谢任何帮助
首先,没有办法直接,例如在 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)