Запрос "один ко многим", выбирающий всех родителей и один верхний дочерний элемент для каждого родителя
Существуют две таблицы SQL:
Parents:
+--+---------+
|id| text |
+--+---------+
| 1| Blah |
| 2| Blah2 |
| 3| Blah3 |
+--+---------+
Childs
+--+------+-------+
|id|parent|feature|
+--+------+-------+
| 1| 1 | 123 |
| 2| 1 | 35 |
| 3| 2 | 15 |
+--+------+-------+
Я хочу выбрать с одним запросом каждую строку из таблицы "Родители" и для каждой отдельной строки из таблицы "Дети" с соотношением "родительский" - "id" и наибольшее значение столбца "функция". В этом примере результат должен быть:
+----+------+----+--------+---------+
|p.id|p.text|c.id|c.parent|c.feature|
+----+------+----+--------+---------+
| 1 | Blah | 1 | 1 | 123 |
| 2 | Blah2| 3 | 2 | 15 |
| 3 | Blah3|null| null | null |
+----+------+----+--------+---------+
Где p = родительская таблица и c = таблица для детей
Я попытался ЛЕВАТЬ ВНУТРЕННУЮ СОЕДИНЕНИЕ И ГРУППУ, но MSSQL Express сказал мне, что запрос с GROUP BY требует функции Aggregate для каждого поля, отличного от Groupped. И я не хочу группировать их всех, а скорее выбирать верхнюю строку (с пользовательским упорядочением).
Я полностью из идей...
Ответы
Ответ 1
select p.id, p.text, c.id, c.parent, c.feature
from Parents p
left join (select c1.id, c1.parent, c1.feature
from Childs c1
join (select p1.id, max(c2.feature) maxFeature
from Parents p1
left join Childs c2 on p1.id = c2.parent
group by p1.id) cf on c1.parent = cf.id
and c1.feature = cf.maxFeature) c
on p.id = c.parent
Ответ 2
Использование CTE (SQL Server 2005 +):
WITH max_feature AS (
SELECT c.id,
c.parent,
MAX(c.feature) 'feature'
FROM CHILD c
GROUP BY c.id, c.parent)
SELECT p.id,
p.text,
mf.id,
mf.parent,
mf.feature
FROM PARENT p
LEFT JOIN max_feature mf ON mf.parent = p.id
Не эквивалент CTE:
SELECT p.id,
p.text,
mf.id,
mf.parent,
mf.feature
FROM PARENT p
LEFT JOIN (SELECT c.id,
c.parent,
MAX(c.feature) 'feature'
FROM CHILD c
GROUP BY c.id, c.parent) mf ON mf.parent = p.id
В вашем вопросе отсутствуют детали для обработки таймерных выключателей (когда значения 2+ CHILD.id
имеют одинаковое значение функции). Ответ агента_9191 использует TOP 1
, но это займет первое, которое будет возвращено, а не обязательно то, которое вы хотите.
Ответ 3
Это должно работать:
SELECT p.id, p.text, c.id, c.parent,c.feature
FROM parent p
LEFT OUTER JOIN (SELECT TOP 1 child.id,
child.parent,
MAX(child.feature)
FROM child
WHERE child.parent = p.id
GROUP BY child.id, child.parent
) c ON p.id = c.parent
Ответ 4
запрос manji не обрабатывает тай-брейки для функции max. Вот мой метод, который я тестировал:
;WITH WithClause AS (SELECT p.id, p.text,
(SELECT TOP 1 c.id from childs c
where c.parent = p.id order by c.feature desc)
AS BestChildID
FROM Parents p)
SELECT WithClause.id, WithClause.text, c.id, c.parent, c.feature
FROM WithClause
LEFT JOIN childs c on WithClause.BestChildID = c.id
Ответ 5
Если вам нужно присоединиться к другому столбцу MAX и любым столбцам, описанным в группе, закрыв вложенный выбор, вы можете использовать функцию APPLY.
Это простейшее решение. Вы также можете использовать оператор WITH. Но это выглядит сложнее.
SELECT p.id, p.text, CHILD_ROW.ANY_COLLUMN
FROM parent p
OUTER APPLY (SELECT TOP 1 child.ANY_COLLUMN
FROM child
WHERE child.parent = p.id
ORDER BY child.feature DESC
) CHILD_ROW