Цепочка связанных таблиц в Rust
Иногда нужны поля из нескольких таблиц подряд: пост ссылается на пользователя, а метки висят на посте через таблицу пар. Прямого столбца от пользователя к метке не требуется, достаточно цепочки связей.
Цепочка соединяет таблицы по очереди:
сначала пост к пользователю, затем
post_tag к посту, затем метку
к post_tag. Каждый шаг сравнивает
номера в условии ON.
Подготовим данные и выведем имя пользователя и имя метки:
use rusqlite::{Connection, params};
fn chain_demo() -> Result<(), rusqlite::Error> {
let _ = std::fs::remove_file("app.db");
let conn = Connection::open("app.db")?;
conn.execute("PRAGMA foreign_keys = ON", [])?;
conn.execute(
"CREATE TABLE user (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
)",
[],
)?;
conn.execute(
"CREATE TABLE post (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL,
title TEXT NOT NULL,
FOREIGN KEY (user_id) REFERENCES user(id)
)",
[],
)?;
conn.execute(
"CREATE TABLE tag (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
)",
[],
)?;
conn.execute(
"CREATE TABLE post_tag (
post_id INTEGER NOT NULL,
tag_id INTEGER NOT NULL,
FOREIGN KEY (post_id) REFERENCES post(id),
FOREIGN KEY (tag_id) REFERENCES tag(id)
)",
[],
)?;
conn.execute("INSERT INTO user (name) VALUES (?1)", params!["ann"])?;
conn.execute(
"INSERT INTO post (user_id, title) VALUES (?1, ?2)",
params![1, "note"],
)?;
conn.execute("INSERT INTO tag (name) VALUES (?1)", params!["news"])?;
conn.execute(
"INSERT INTO post_tag (post_id, tag_id) VALUES (?1, ?2)",
params![1, 1],
)?;
let mut stmt = conn.prepare(
"SELECT u.name, t.name
FROM user u
INNER JOIN post p ON p.user_id = u.id
INNER JOIN post_tag pt ON pt.post_id = p.id
INNER JOIN tag t ON pt.tag_id = t.id
ORDER BY p.id",
)?;
let rows = stmt.query_map([], |row| {
let user_name: String = row.get(0)?;
let tag_name: String = row.get(1)?;
Ok((user_name, tag_name))
})?;
for item in rows {
let (user_name, tag_name) = item?;
println!("{} {}", user_name, tag_name);
}
Ok(())
}
fn main() {
match chain_demo() {
Ok(()) => {}
Err(e) => println!("{}", e),
}
}
Фильтр по имени пользователя ставят после всех шагов цепочки:
use rusqlite::{Connection, params};
fn tags_for_ann() -> Result<(), rusqlite::Error> {
let _ = std::fs::remove_file("app.db");
let conn = Connection::open("app.db")?;
conn.execute("PRAGMA foreign_keys = ON", [])?;
conn.execute(
"CREATE TABLE user (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
)",
[],
)?;
conn.execute(
"CREATE TABLE post (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL,
title TEXT NOT NULL,
FOREIGN KEY (user_id) REFERENCES user(id)
)",
[],
)?;
conn.execute(
"CREATE TABLE tag (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
)",
[],
)?;
conn.execute(
"CREATE TABLE post_tag (
post_id INTEGER NOT NULL,
tag_id INTEGER NOT NULL,
FOREIGN KEY (post_id) REFERENCES post(id),
FOREIGN KEY (tag_id) REFERENCES tag(id)
)",
[],
)?;
conn.execute("INSERT INTO user (name) VALUES (?1)", params!["ann"])?;
conn.execute("INSERT INTO user (name) VALUES (?1)", params!["bob"])?;
conn.execute(
"INSERT INTO post (user_id, title) VALUES (?1, ?2)",
params![1, "a"],
)?;
conn.execute(
"INSERT INTO post (user_id, title) VALUES (?1, ?2)",
params![2, "b"],
)?;
conn.execute("INSERT INTO tag (name) VALUES (?1)", params!["news"])?;
conn.execute("INSERT INTO tag (name) VALUES (?1)", params!["draft"])?;
conn.execute(
"INSERT INTO post_tag (post_id, tag_id) VALUES (?1, ?2)",
params![1, 1],
)?;
conn.execute(
"INSERT INTO post_tag (post_id, tag_id) VALUES (?1, ?2)",
params![2, 2],
)?;
let mut stmt = conn.prepare(
"SELECT t.name
FROM user u
INNER JOIN post p ON p.user_id = u.id
INNER JOIN post_tag pt ON pt.post_id = p.id
INNER JOIN tag t ON pt.tag_id = t.id
WHERE u.name = ?1",
)?;
let rows = stmt.query_map(params!["ann"], |row| {
let tag_name: String = row.get(0)?;
Ok(tag_name)
})?;
for item in rows {
let tag_name = item?;
println!("{}", tag_name);
}
Ok(())
}
fn main() {
match tags_for_ann() {
Ok(()) => {}
Err(e) => println!("{}", e),
}
}
Даны таблицы:
CREATE TABLE user (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE post (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL,
title TEXT NOT NULL,
FOREIGN KEY (user_id) REFERENCES user(id)
);
CREATE TABLE tag (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE post_tag (
post_id INTEGER NOT NULL,
tag_id INTEGER NOT NULL,
FOREIGN KEY (post_id) REFERENCES post(id),
FOREIGN KEY (tag_id) REFERENCES tag(id)
);
Товар лежит в подкатегории, подкатегория в категории. Опишите три таблицы и столбцы ссылок без текста запроса выборки.
Даны user, post, tag
и post_tag как в примере урока.
Напишите запрос, который выведет имя
пользователя, заголовок поста и имя
метки для каждой связанной тройки.
Скажите, сколько шагов соединения нужно,
чтобы из user получить и заголовок
поста, и имя метки через post_tag.