一对多查询为每个父母选择所有父母和单个***孩子

Posted

技术标签:

【中文标题】一对多查询为每个父母选择所有父母和单个***孩子【英文标题】:one-to-many query selecting all parents and single top child for each parent 【发布时间】:2009-10-13 21:54:36 【问题描述】:

有两个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 = 子表

我尝试使用 LEFT OUTER JOIN 和 GROUP BY,但 MSSQL Express 告诉我使用 GROUP BY 的查询需要在每个非分组字段上使用聚合函数。而且我不想将它们全部分组,而是选择顶行(使用自定义排序)。

我完全没有想法......

【问题讨论】:

【参考方案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 值具有相同的特征值时)。 Agent_9191 的答案使用TOP 1,但这将采用返回的第一个而不一定是您想要的。

【讨论】:

我认为按 (c.id, c.parent) 分组就像返回原始表 (Childs),因为父级不能拥有具有 2 个特征的同一个子级。【参考方案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 的查询不处理最大功能的决胜局。这是我测试过的方法:

;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

【讨论】:

OMG Ponies 的方法也不起作用,我测试过,我用他的 sn-ps 得到了太多行,正如 manji 所指出的那样。【参考方案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

【讨论】:

以上是关于一对多查询为每个父母选择所有父母和单个***孩子的主要内容,如果未能解决你的问题,请参考以下文章

使用 from 子句优化的 SQL 更新查询

每个熊孩子背后都站着一对熊父母

jquery选择器需要选择父母的所有某些孩子

如何使用 PDO 从 PHP 中的单个 select 语句将孩子分配给父母?

让所有父母为孩子

Core Data 中父实体的计算属性