Соединение строк таблиц в Go
Когда данные разнесены по таблицам, отчет часто требует поля с обеих сторон. Внутреннее соединение складывает строки двух таблиц, у которых совпал ключ: номер в дочерней строке равен номеру в родительской.
Условие совпадения пишут после слова
ON. Псевдонимы таблиц сокращают
имена столбцов в длинном тексте запроса.
Выведем каждый заказ вместе с именем владельца:
package main
import (
"database/sql"
"fmt"
"log"
"os"
_ "modernc.org/sqlite"
)
func main() {
os.Remove("shop.db")
db, err := sql.Open("sqlite", "shop.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
_, err = db.Exec("PRAGMA foreign_keys = ON")
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(`
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
)
`)
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(`
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL,
title TEXT NOT NULL,
FOREIGN KEY (user_id) REFERENCES users (id)
)
`)
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(`INSERT INTO users (name) VALUES (?)`, "ann")
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(
`INSERT INTO orders (user_id, title) VALUES (?, ?)`,
1, "pen",
)
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(
`INSERT INTO orders (user_id, title) VALUES (?, ?)`,
1, "book",
)
if err != nil {
log.Fatal(err)
}
rows, err := db.Query(`
SELECT u.name, o.title
FROM orders o
INNER JOIN users u ON o.user_id = u.id
ORDER BY o.id
`)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var userName string
var orderTitle string
err = rows.Scan(&userName, &orderTitle)
if err != nil {
log.Fatal(err)
}
fmt.Println(userName, orderTitle)
}
err = rows.Err()
if err != nil {
log.Fatal(err)
}
}
Фильтр по имени пользователя ставят в
WHERE уже после соединения:
package main
import (
"database/sql"
"fmt"
"log"
"os"
_ "modernc.org/sqlite"
)
func main() {
os.Remove("shop.db")
db, err := sql.Open("sqlite", "shop.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
_, err = db.Exec("PRAGMA foreign_keys = ON")
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(`
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
)
`)
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(`
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL,
title TEXT NOT NULL,
FOREIGN KEY (user_id) REFERENCES users (id)
)
`)
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(`INSERT INTO users (name) VALUES (?)`, "ann")
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(`INSERT INTO users (name) VALUES (?)`, "bob")
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(
`INSERT INTO orders (user_id, title) VALUES (?, ?)`,
1, "pen",
)
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(
`INSERT INTO orders (user_id, title) VALUES (?, ?)`,
2, "lamp",
)
if err != nil {
log.Fatal(err)
}
rows, err := db.Query(`
SELECT o.title
FROM orders o
INNER JOIN users u ON o.user_id = u.id
WHERE u.name = ?
`, "ann")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var orderTitle string
err = rows.Scan(&orderTitle)
if err != nil {
log.Fatal(err)
}
fmt.Println(orderTitle)
}
err = rows.Err()
if err != nil {
log.Fatal(err)
}
}
Даны таблицы и данные:
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL,
title TEXT NOT NULL,
FOREIGN KEY (user_id) REFERENCES users (id)
);
INSERT INTO users (name) VALUES ('ann');
INSERT INTO orders (user_id, title) VALUES (1, 'pen');
Напишите запрос, который выведет имя пользователя и название заказа в одной строке результата для каждой пары.
Даны таблицы garages и vehicles
с полем garage_id у машины. Составьте
текст запроса, который выведет код гаража
и номер машины для каждой связанной пары.
Даны те же users и orders,
что в примере с фильтром. Опишите словами,
какие заказы попадут в выборку, если в
условии имя "bob".