一对多查询为每个父母选择所有父母和单个***孩子
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
【讨论】:
以上是关于一对多查询为每个父母选择所有父母和单个***孩子的主要内容,如果未能解决你的问题,请参考以下文章