SQL Server:与 TOP 1 的 LEFT OUTER JOIN 最多选择一行
Posted
技术标签:
【中文标题】SQL Server:与 TOP 1 的 LEFT OUTER JOIN 最多选择一行【英文标题】:SQL Server: LEFT OUTER JOIN with TOP 1 to Select at Most One Row 【发布时间】:2012-03-06 22:23:30 【问题描述】:我基本上需要对 2 个表(CarePlan 和推荐人)进行左外连接问题是我需要最新的推荐人如果存在,如果不存在也没关系。
我有这 2 个问题 1. 加入 CarePlan/Referral 表 - 如果一个护理计划有多个推荐,或者根本没有推荐信息(左外连接),则创建重复的护理计划 2. 给定 CarePlanId,根据日期选择前 1 个推荐人
我想将这两个结合起来,所以我会获取所有的护理计划及其转介(如果存在),如果存在 - 只接受最新的转介
select * from CarePlan c //query 1
left outer join Referral r on
r.CarePlanId = c.CarePlanId
select top 1 * from Referral r //query 2
where r.CarePlanId = '1'
order by ReferralDate desc
编辑:
第一个查询给了我这样的信息:
CarePlanID ReferralId ReferralDate
---------- ---------- ------------
1 1 05/15/12
2 NULL NULL
1 2 05/10/12 //Old date, dont want this careplan
第二个查询会给我最新日期的推荐
ReferralId ReferralDate
---------- ------------
1 05/15/12
推荐数据,可能有 0 个或多个属于 Careplan 的推荐
ReferralID CarePlanId Date
---------- ---------- ----
1 1 05/15/12
2 1 05/10/12
最终我想要一个查询,它可以为我提供具有最新日期的推荐的护理计划,如果没有,则推荐为 null
像这样:
CarePlanId ReferralId ReferralDate
---------- ---------- ------------
1 1 05/15/12
2 NULL NULL
谢谢 - 我希望这是有道理的
【问题讨论】:
在阅读您想要的内容时遇到问题,但我认为您需要查找如何制作 cte 或派生表。 你能展示样本数据和想要的结果吗?像@HLGEM 一样,不清楚你从哪里开始,你想在哪里结束。 【参考方案1】:select *
from CarePlan c
outer apply (
select top 1 * --top N rows
from Referral r
where r.CarePlanId = c.CarePlanId --join condition
order by /*fill this in!*/
) x
请注意,由于直到 2014 版(包括版本 2014)的优化器弱点,这会强制循环连接。
【讨论】:
【参考方案2】:我知道这个问题比较老,但我觉得还有另一种方法未被充分利用:
您可以将表连接回自身并使用运算符查找“最新”记录。
回答
SELECT CP.CarePlanId, R.ReferralId, R.ReferralDate
FROM CarePlan CP
LEFT OUTER JOIN Referral R ON R.CarePlanId = CP.CarePlanId
LEFT OUTER JOIN Referral R_NEWER ON R.CarePlanId = R_NEWER.CarePlanId AND R.ReferralDate < R_NEWER.ReferralDate
WHERE R_NEWER.ReferralId IS NULL
结果:
CP.CarePlanId R.ReferralId R.ReferralDate
---------- ---------- ------------
1 1 05/15/12
2 NULL NULL
说明
让我们分解一下。您基本上是在说,对于每个推荐记录,(左外)加入与相同 CarePlanId 相关联的所有其他推荐记录,但仅在有更新的 ReferralDate 的情况下。
这是没有 where 子句的查询(以及来自 R_NEWER 表的一些附加信息):
SELECT CP.CarePlanId, R.ReferralId, R.ReferralDate, R_NEWER.ReferralId, R.NEWER.ReferralDate
FROM CarePlan CP
LEFT OUTER JOIN Referral R ON R.CarePlanId = CP.CarePlanId
LEFT OUTER JOIN Referral R_NEWER ON R.CarePlanId = R_NEWER.CarePlanId AND R.ReferralDate < R_NEWER.ReferralDate
这是该查询的结果:
CP.CarePlanId R.ReferralId R.ReferralDate R_NEWER.ReferralId R_NEWER.ReferralDate
---------- ---------- ------------ ------------ ------------
1 1 05/15/12 NULL NULL
2 NULL NULL NULL NULL
1 2 05/10/12 1 05/15/12
如您所见,只有推荐 ID 2(上面的第 3 条记录)在推荐表中找到要加入的“更新”记录(即推荐 ID 1)。推荐人 ID 1(上面的第一条记录)没有找到“较新”的推荐人(对于相同的 CarePlanId)。
因此,考虑到这一点,现在我们只需将 where 子句添加回去:
SELECT CP.CarePlanId, R.ReferralId, R.ReferralDate, R_NEWER.ReferralId, R.NEWER.ReferralDate
FROM CarePlan CP
LEFT OUTER JOIN Referral R ON R.CarePlanId = CP.CarePlanId
LEFT OUTER JOIN Referral R_NEWER ON R.CarePlanId = R_NEWER.CarePlanId AND R.ReferralDate < R_NEWER.ReferralDate
WHERE R_NEWER.ReferralId IS NULL
然后得到:
CP.CarePlanId R.ReferralId R.ReferralDate R_NEWER.ReferralId R_NEWER.ReferralDate
---------- ---------- ------------ ------------ ------------
1 1 05/15/12 NULL NULL
2 NULL NULL NULL NULL
此时,只需从您的 SELECT 中删除您的 R_NEWER 列,因为它们不再需要并且您有答案。
重要的是要记住,“where”在连接发生后应用,但 ON 语句发生在连接时。为了让我更容易理解这一点,我总是尝试编写 SELECT 和 JOIN,并从我要加入的每个表中返回列,然后在我清楚地了解返回的内容后添加我的 WHERE 子句。
警告 这种方法在大多数情况下效果很好,但如果您有 2 个推荐人(对于相同的 CarePlanId),日期为 05/15/12 并且该日期是“最近的”,则可能会有重复的行。为了解决这个问题,如果出现这种情况,您可以根据“最高”的 ReferralId 扩展您的联接以限制。
【讨论】:
5 年前回答,但这个答案是迄今为止最好的,也适用于 JPA 2.1。【参考方案3】:只是猜测。我不确定 EF 是否会遇到 CTE 语法问题——你能强制 EF 调用存储过程,这样你就不会被 EF 支持的功能子集束缚吗?
;WITH r AS
(
SELECT CarePlanId, MAX(ReferralDate)
FROM dbo.Referrals GROUP BY CarePlanId
)
SELECT * FROM dbo.CarePlan AS c
LEFT OUTER JOIN r
ON r.CarePlanId = c.CarePlanId;
【讨论】:
抱歉——我想这更像是一个一般的 SQL 问题,实体框架在这里不重要——至少我希望如此。我也希望返回的护理计划是不同的(只使用最新推荐的) 只有你可以告诉我们后者 - 你真的运行查询吗?它会返回您期望的结果吗?如果您向我们展示示例数据以及您对输出的期望,我们可以更轻松地尝试我们的答案,然后再将它们扔给您。它还可以节省每个人的时间。以上是关于SQL Server:与 TOP 1 的 LEFT OUTER JOIN 最多选择一行的主要内容,如果未能解决你的问题,请参考以下文章
SQL Server 中的 LEFT JOIN 与 LEFT OUTER JOIN
INNER JOIN与LEFT JOIN在SQL Server的性能
SQL GROUP BY 与 LEFT JOIN MS SQL Server