Запрос на родительское / дочернее объединение с и без условий для дочернего - PullRequest
0 голосов
/ 25 января 2020

Схема и примеры данных

Простые отношения один-ко-многим родитель / ребенок:

CREATE TABLE Parent (
  id VARCHAR PRIMARY KEY
);

CREATE TABLE Child (
  id VARCHAR PRIMARY KEY,
  type INT,
  parent_id VARCHAR REFERENCES Parent(id)
);

INSERT INTO Parent VALUES ('p1'), ('p2'), ('p3'), ('p4');
INSERT INTO Child VALUES ('c1', 0, 'p2'), ('c2', 42, 'p2'), ('c3', 0, 'p3'), ('c4', 42, 'p4');

SQL Скрипка

Запросы

Мы всегда запрашиваем родителей со своими детьми. Когда конкретный родитель является частью результата, все его дочерние элементы также должны быть извлечены, в любом случае.

Вариант использования A)

Нет ограничений для дочерних элементов - просто отобразите все данные родителей / детей, включая родителей без детей (p1).

Запрос LEFT JOIN

SELECT p.*, c.* FROM parent p
LEFT JOIN child c ON c.parent_id = p.id

дает желаемый результат:

id|id|type|
--|--|----|
p1|  |    |
p2|c1|   0|
p2|c2|  42|
p3|c3|   0|
p4|c4|  42|

Вариант использования B)

Существует ограничение для детей, например type = 42. Мы хотели бы получить родителей (с всеми их детьми), у которых есть любого ребенка с type = 42, т.е. желаемый результат:

id|id|type|
--|--|----|
p2|c1|   0|
p2|c2|  42|
p4|c4|  42|

Обратите внимание, что c1 извлекается, потому что у его родителя p2 есть соответствующий дочерний элемент c2, хотя тип c1 не соответствует.

Этот запрос с коррелированным подзапросом достигает этого результата:

SELECT p.*, c.* FROM parent p
JOIN child c ON c.parent_id = p.id
WHERE EXISTS (SELECT 1 FROM child c2 WHERE c2.parent_id = p.id AND c2.type = 42)

Вопросы

1) Является ли второй запрос WHERE EXISTS стандартным способом go для варианта использования B)?

2) Поскольку в действительности жизнь, существует несколько ассоциаций и несколько возможных условий фильтрации, есть ли способ упростить всю конструкцию запроса, охватывающую оба варианта использования A) и B)? В смысле «основного запроса», который может быть «обогащен» условиями для различных дочерних ассоциаций. В случае с одной родительской таблицей существует «главный запрос»

SELEECT * from Parent p
WHERE 1 = 1

. Любые условия для родительского элемента можно просто AND 'отредактировать в предложении WHERE «основного запроса». , что делает эту конструкцию запроса очень простой.

Есть ли что-нибудь сопоставимое для объединений с различными условиями для разных потомков, сохраняя семантику "всегда возвращать всех потомков для каждого возвращенного родителя"?

1 Ответ

0 голосов
/ 25 января 2020

Я думаю, что вы можете изменить свой второй запрос, чтобы переместить условие exists в предложение on:

SELECT p.*, c.*
FROM parent p LEFT JOIN
     child c
     ON c.parent_id = p.id AND
        EXISTS (SELECT 1
                FROM child c2
                WHERE c2.parent_id = p.id AND
                      c2.type = 42
               );

Тем не менее, это проще с помощью оконных функций:

SELECT p.*, c.*
FROM parent p LEFT JOIN
     (SELECT c.*,
             SUM(CASE WHEN c.type = 42 THEN 1 ELSE 0 END) OVER (PARTITION BY c.parent_id) as num_42
      FROM child c
     ) c
     ON c.parent_id = p.id AND
        num_42 > 0;
...