#postgresql-13
Вопрос:
Я хотел бы заранее извиниться, потому что я не смог найти правильное / точное имя для этого вопроса.. complex query
я имею в виду, что отношения между таблицами являются либо one to many
или many to many
.
В принципе, что меня интересует — если в запросе есть a join
, и это соединение имеет свой собственный select
оператор — достаточно ли умна база данных, чтобы выполнить весь этот оператор один раз, или она запрашивает каждый select
оператор в join
предложении отдельно?
Вот пример того, чего я хочу достичь. У нас есть 3 таблицы:
CREATE TABLE persons (id int, name text, pets_ids int[]);
INSERT INTO persons VALUES (1, 'John', '{}'), (2, 'Jill', '{1,2,3}'), (3, 'Mary', '{2}');
CREATE TABLE pets (id int, name text);
INSERT INTO pets VALUES (1,'Fluffy'),(2,'Buster'),(3,'Doggy');
CREATE TABLE personFriendhips (person_id int, person_friend_id int);
INSERT INTO personFriendhips VALUES (1, 2), (1, 3), (2, 3);
Я хотел бы собрать статистические данные в одном запросе для всех пользователей: идентификатор пользователя, имя, количество друзей и имена домашних животных.
Проблема в том, что нет никакой связи между таблицами friendships и pets, поэтому, если бы я выполнил 2 объединения в одном и том же операторе, я бы в итоге получил неправильное количество друзей (см. Скрипку ниже).:
SELECT
per1.id
,per1.name
,count(fr1.person_friend_id) as friendsCount
,array_agg(pet1.name::TEXT) as petNames
FROM persons per1
left outer join personFriendhips fr1 ON per1.id = fr1.person_id
left outer join pets pet1 on (ARRAY[pet1.id] <@ per1.pets_ids)
group by per1.id, per1.name, fr1.person_id
order by per1.id
;
Результаты
ID | Имя | количество друзей | имена домашних животных |
---|---|---|---|
1 | Джон | 2 | [ноль, ноль] |
2 | Джилл | 3 | [«Пушистый», «Бастер», «Собачий»] |
3 | Мэри | 0 | [«Buster»] |
Поэтому вместо этого я должен указать запросу агрегировать имена домашних животных отдельно от остальной части инструкции:
SELECT
per1.id
,per1.name
,count(fr1.person_friend_id) as friendsCount
,pet2.petNames
FROM persons per1
left outer join personFriendhips fr1 ON per1.id = fr1.person_id
left outer join (
SELECT
per2.id
,array_agg(pet1.name::TEXT) as petNames
FROM persons per2
left outer join pets pet1 on (ARRAY[pet1.id] <@ per2.pets_ids)
group by per2.id) as pet2 on pet2.id = per1.id
group by per1.id, per1.name, fr1.person_id, pet2.petNames
order by per1.id
;
results
id | name | friendscount | petnames |
---|---|---|---|
1 | John | 2 | [null] |
2 | Jill | 1 | [«Fluffy»,»Buster»,»Doggy»] |
3 | Mary | 0 | [«Buster»] |
So here is my concern — If I have 1000 persons in results — does that mean that the join
subquery was executed separately for each person (1000 times total), or is the db engine smart enough to recognize what I am trying to achieve and merges all joins, so that the single statement is executed? (a.k.a. first query style, but without duplicates)
you can find the fiddle here: https://www.db-fiddle.com/f/6diyQPTE5WLzgD693RZcJ3/0
I would like to make a query that is being run as a «single entity» rather than 2 entities running separately and joined together later.