запрос «один ко многим», выбирающий всех родителей и одного старшего ребенка для каждого родителя - PullRequest
20 голосов
/ 14 октября 2009

Есть две таблицы SQL:

Parents:
+--+---------+
|id|   text  |
+--+---------+
| 1|  Blah   |
| 2|  Blah2  |
| 3|  Blah3  |
+--+---------+

Childs
+--+------+-------+
|id|parent|feature|
+--+------+-------+
| 1|   1  |  123  |
| 2|   1  |   35  |
| 3|   2  |   15  |
+--+------+-------+

Я хочу одним запросом выбрать каждую строку из таблицы Parents и для каждой отдельной строки из таблицы Childs с отношением "parent" - значением "id" и наибольшим значением столбца "feature". В этом примере результат должен быть:

+----+------+----+--------+---------+
|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 = дочерняя таблица

Я пытался УСТРАНИТЬ ВНЕШНЕЕ СОЕДИНЕНИЕ и GROUP BY, но MSSQL Express сказал мне, что для запроса с GROUP BY требуются функции Aggregate для всех незагруженных полей. И я не хочу группировать их все, а выбрать верхний ряд (с пользовательским порядком).

У меня совершенно нет идей ...

Ответы [ 5 ]

17 голосов
/ 14 октября 2009
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
9 голосов
/ 14 октября 2009

Использование 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 имеют одинаковое значение свойства). В ответе Agent_9191 используется TOP 1, но при этом возвращается первое возвращаемое, а не обязательно то, что вам нужно.

4 голосов
/ 14 октября 2009

Это должно работать:

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
3 голосов
/ 17 июня 2014

запрос Манджи не обрабатывает тай-брейки для максимальной функции. Вот мой метод, который я протестировал:

;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
1 голос
/ 11 сентября 2017

Если вам нужно объединить отличный от 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
...