Цепочка из трех таблиц в 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 goods (
id INTEGER PRIMARY KEY,
title TEXT NOT NULL
)
`)
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(`
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL,
good_id INTEGER NOT NULL,
FOREIGN KEY (user_id) REFERENCES users (id),
FOREIGN KEY (good_id) REFERENCES goods (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 goods (title) VALUES (?)`, "pen")
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(
`INSERT INTO orders (user_id, good_id) VALUES (?, ?)`,
1, 1,
)
if err != nil {
log.Fatal(err)
}
rows, err := db.Query(`
SELECT u.name, o.id, g.title
FROM orders o
INNER JOIN users u ON o.user_id = u.id
INNER JOIN goods g ON o.good_id = g.id
ORDER BY o.id
`)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var userName string
var orderID int
var goodTitle string
err = rows.Scan(&userName, &orderID, &goodTitle)
if err != nil {
log.Fatal(err)
}
fmt.Println(userName, orderID, goodTitle)
}
err = rows.Err()
if err != nil {
log.Fatal(err)
}
}
Фильтр по имени пользователя ставят после обоих шагов цепочки:
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 goods (
id INTEGER PRIMARY KEY,
title TEXT NOT NULL
)
`)
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(`
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL,
good_id INTEGER NOT NULL,
FOREIGN KEY (user_id) REFERENCES users (id),
FOREIGN KEY (good_id) REFERENCES goods (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 goods (title) VALUES (?)`, "pen")
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(`INSERT INTO goods (title) VALUES (?)`, "book")
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(
`INSERT INTO orders (user_id, good_id) VALUES (?, ?)`,
1, 1,
)
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(
`INSERT INTO orders (user_id, good_id) VALUES (?, ?)`,
2, 2,
)
if err != nil {
log.Fatal(err)
}
rows, err := db.Query(`
SELECT g.title
FROM orders o
INNER JOIN users u ON o.user_id = u.id
INNER JOIN goods g ON o.good_id = g.id
WHERE u.name = ?
`, "ann")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var goodTitle string
err = rows.Scan(&goodTitle)
if err != nil {
log.Fatal(err)
}
fmt.Println(goodTitle)
}
err = rows.Err()
if err != nil {
log.Fatal(err)
}
}
Даны таблицы:
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE goods (
id INTEGER PRIMARY KEY,
title TEXT NOT NULL
);
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL,
good_id INTEGER NOT NULL,
FOREIGN KEY (user_id) REFERENCES users (id),
FOREIGN KEY (good_id) REFERENCES goods (id)
);
Товар лежит в подкатегории, подкатегория в категории. Опишите три таблицы и столбцы ссылок без текста запроса выборки.
Даны users, orders и
goods как в примере урока. Напишите
запрос, который выведет имя пользователя,
номер заказа и название товара для каждой
строки заказа.
Скажите, сколько шагов соединения нужно,
чтобы из orders получить и имя
пользователя, и название товара.