Выборка через таблицу связи в C++
Запрос, который вытащит юзеров вместе с их городами, делает два соединения: первое присоединит к юзерам таблицу связи, а второе по связям присоединит города:
SELECT
users.name AS user_name, cities.name AS city_name
FROM
users
LEFT JOIN users_cities
ON users_cities.user_id = users.id
LEFT JOIN cities
ON users_cities.city_id = cities.id
Результат запроса
Результат содержит имя каждого юзера столько раз, со сколькими городами он связан - по одной строке на пару юзер-город.
Удобнее сгруппировать города в контейнер по имени юзера и затем вывести сводку. Сделаем это на небольшой базе с тремя юзерами и таблицей связи:
#include <iostream>
#include <map>
#include <sqlite3.h>
#include <string>
#include <vector>
int main() {
sqlite3* db = nullptr;
if (sqlite3_open("lnk_users.db", &db) != SQLITE_OK) {
std::cerr << sqlite3_errmsg(db) << "\n";
sqlite3_close(db);
return 1;
}
sqlite3_exec(
db, "PRAGMA foreign_keys = ON",
nullptr, nullptr, nullptr
);
sqlite3_exec(
db,
"CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)",
nullptr, nullptr, nullptr
);
sqlite3_exec(
db,
"CREATE TABLE cities (id INTEGER PRIMARY KEY, name TEXT)",
nullptr, nullptr, nullptr
);
sqlite3_exec(
db,
"CREATE TABLE users_cities ("
"id INTEGER PRIMARY KEY, user_id INTEGER, city_id INTEGER, "
"FOREIGN KEY (user_id) REFERENCES users (id), "
"FOREIGN KEY (city_id) REFERENCES cities (id))",
nullptr, nullptr, nullptr
);
sqlite3_exec(
db,
"INSERT INTO users (name) VALUES ('user1'), ('user2'), ('user3')",
nullptr, nullptr, nullptr
);
sqlite3_exec(
db,
"INSERT INTO cities (name) VALUES "
"('city1'), ('city2'), ('city3')",
nullptr, nullptr, nullptr
);
sqlite3_exec(
db,
"INSERT INTO users_cities (user_id, city_id) VALUES "
"(1,1), (1,2), (2,1), (2,3), (3,2)",
nullptr, nullptr, nullptr
);
sqlite3_stmt* sel = nullptr;
sqlite3_prepare_v2(
db,
"SELECT users.name AS user_name, cities.name AS city_name "
"FROM users "
"LEFT JOIN users_cities ON users_cities.user_id = users.id "
"LEFT JOIN cities ON users_cities.city_id = cities.id",
-1, &sel, nullptr
);
std::map<std::string, std::vector<std::string>> grouped;
while (sqlite3_step(sel) == SQLITE_ROW) {
const char* user = reinterpret_cast<const char*>(
sqlite3_column_text(sel, 0)
);
if (sqlite3_column_type(sel, 1) != SQLITE_NULL) {
const char* city = reinterpret_cast<const char*>(
sqlite3_column_text(sel, 1)
);
grouped[user].push_back(city);
}
}
sqlite3_finalize(sel);
for (const auto& pair : grouped) {
std::cout << pair.first << ": ";
for (std::size_t i = 0; i < pair.second.size(); ++i) {
if (i > 0) {
std::cout << ", ";
}
std::cout << pair.second[i];
}
std::cout << "\n";
}
sqlite3_close(db);
return 0;
}
Напишите запрос, который достанет товары вместе с их категориями. Товар может принадлежать нескольким категориям.
Выведите полученные данные в консоль так, чтобы в каждой строке сначала стояло имя продукта, а после двоеточия через запятую перечислялись категории этого продукта:
product1: category1, category2, category3
product2: category1, category3
product3: category1