Группы в запросе в Go
Группа делит строки по одинаковому
значению столбца и считает агрегат
внутри каждой части. В тексте запроса
после списка полей пишут GROUP BY
с именем столбца группы.
В выборке обычно показывают столбец
группы и функцию вроде COUNT
или SUM. Результат читают
циклом, если групп несколько.
Посчитаем, сколько раз встречается каждое название в заказах:
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.Ping()
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(`
CREATE TABLE orders (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
qty INTEGER NOT NULL,
price REAL NOT NULL
)
`)
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(
`INSERT INTO orders (title, qty, price) VALUES (?, ?, ?)`,
"mug", 1, 9.0,
)
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(
`INSERT INTO orders (title, qty, price) VALUES (?, ?, ?)`,
"mug", 2, 9.0,
)
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(
`INSERT INTO orders (title, qty, price) VALUES (?, ?, ?)`,
"pen", 5, 2.0,
)
if err != nil {
log.Fatal(err)
}
rows, err := db.Query(`
SELECT title, COUNT(*) FROM orders GROUP BY title
`)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var title string
var cnt int
err = rows.Scan(&title, &cnt)
if err != nil {
log.Fatal(err)
}
fmt.Println(title, cnt)
}
err = rows.Err()
if err != nil {
log.Fatal(err)
}
}
На экране "mug" с 2 и
"pen" с 1. Каждая
группа дала свою пару значений.
Сумму количества по названию посчитаем так же:
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.Ping()
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(`
CREATE TABLE goods (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
price REAL NOT NULL
)
`)
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(
`INSERT INTO goods (title, price) VALUES (?, ?)`,
"pen", 2.0,
)
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(
`INSERT INTO goods (title, price) VALUES (?, ?)`,
"pen", 3.0,
)
if err != nil {
log.Fatal(err)
}
_, err = db.Exec(
`INSERT INTO goods (title, price) VALUES (?, ?)`,
"book", 12.0,
)
if err != nil {
log.Fatal(err)
}
rows, err := db.Query(`
SELECT title, AVG(price) FROM goods GROUP BY title
`)
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var title string
var avg float64
err = rows.Scan(&title, &avg)
if err != nil {
log.Fatal(err)
}
fmt.Println(title, avg)
}
err = rows.Err()
if err != nil {
log.Fatal(err)
}
}
Даны строки orders:
mug qty 1
mug qty 3
cup qty 2
Скажите, что вернет SUM(qty)
с GROUP BY title для "mug".
Составьте запрос к goods,
который для каждого title
покажет COUNT(*) и MAX(price).
Скажите, почему в запросе с группой
нельзя выбрать столбец price
без агрегата, если группируют по
title.