Я хочу запросить базу данных, получить ВСЕ данные пользователя и отправить их на мой внешний интерфейс в объекте JSON (со многими уровнями вложенности).
например
{
user_id: 1,
username: james,
messages: [
{
message_id: 'fewfef',
message: 'lorum ipsum'
... : {
...
}
}
]
}
Пример схемы/данных:
--user table (parent)
CREATE TABLE userdata (
user_id integer,
username text
);
INSERT INTO userdata VALUES (1, 'james');
-- messages table (child) connected to user table
CREATE TABLE messages(
message_id integer,
fk_messages_userdata integer,
message text
);
INSERT INTO messages VALUES (1, 1, 'hello');
INSERT INTO messages VALUES (2, 1, 'lorum ipsum');
INSERT INTO messages VALUES (3, 1, 'test123');
-- querying all data at once
SELECT u.username, m.message_id, m.message FROM userdata u
INNER JOIN messages m
ON u.user_id = m.fk_messages_userdata
WHERE u.user_id = '1';
Это выводит данные так:
username|message_id|message |
--------+----------+-----------+
james | 1|hello |
james | 2|lorum ipsum|
james | 3|test123 |
Проблема в том, что имя пользователя повторяется для каждого сообщения. Для больших баз данных и большего количества уровней вложенности это приведет к тому, что будет запрашиваться/отправляться много бесполезных данных.
Лучше сделать один запрос, чтобы получить все эти данные и отправить их в серверную часть, или сделать отдельный запрос для каждой таблицы и получить только те данные, которые мне нужны?
Например, я мог бы запустить эти запросы:
-- getting only user metadata
SELECT username from userdata WHERE user_id = '1';
-- output
username|
--------+
james |
-- getting only user's messages
SELECT m.message_id, m.message as message_id FROM userdata u
INNER JOIN messages m
ON u.user_id = m.fk_messages_userdata
WHERE u.user_id = '1';
--output
message_id|message_id |
----------+-----------+
1|hello |
2|lorum ipsum|
3|test123 |
Таким образом я получаю только те данные, которые мне нужны, и с ними немного проще работать, так как они более организованы. Но есть ли недостаток в выполнении отдельных запросов вместо одного большого? Есть ли другие способы сделать это?
Лучше сделать один запрос, чтобы получить все эти данные и отправить их в серверную часть, или сделать отдельный запрос для каждой таблицы и получить только те данные, которые мне нужны?
Лучше всего выполнить только один запрос и получить только нужные данные. Пока это не становится слишком сложным - что не IMO:
SELECT to_json(usr)
FROM (
SELECT u.user_id, u.username
, (SELECT json_agg(msg) -- aggregation in correlated subquery
FROM (
SELECT m.message_id, m.message
FROM messages m
WHERE m.fk_messages_userdata = u.user_id
) msg
) AS messages
FROM userdata u
WHERE u.user_id = 1 -- provide user_id here once!
) usr;
Есть много других способов.
(LEFT) JOIN LATERAL
вместо коррелированного подзапроса. Видеть:
json_build_object()
вместо преобразования целых строк из подзапросов. Видеть:
Но эта версия выше должна быть самой короткой и быстрой.
Связанный:
Соединения могут создавать строки результатов с повторяющимися данными, когда задействованы отношения с множественностью :n. Если вы хотите избежать этого, не выполняйте такие соединения. Вместо этого запрашивайте каждую сторону отдельно. JSON достаточно хорошо подходит для представления результирующей многоуровневой структуры данных.