如何加入子查询的第一行?
Posted
技术标签:
【中文标题】如何加入子查询的第一行?【英文标题】:How do I join the first row of a subquery? 【发布时间】:2011-06-09 05:02:25 【问题描述】:我有一个发票表和一个键相关的相关数据的子表。特别是,对于每张发票,我只对子表中的第一个相关行感兴趣。鉴于我希望每个发票键都有一个相关行 - 我该如何完成?
Select i.[Invoice Number],
c.[Carrier Name]
From Invoice i
Left Join Carriers c on i.[InvoiceKey] = c.[InvoiceKey]
Where -- what?
我猜从语义上讲,我正在寻找类似于 Top 1 c.CarrierName Group by InvoiceKey
概念的东西(或者如果在 T-SQL 中可能的话,那将是什么概念。)
我考虑过对子查询进行左连接,但这似乎效率不高。有没有人有任何 T-SQL 技巧来有效地实现这一目标?
编辑:对不起,伙计们,我忘了提到这是 SQL Server 2000,所以虽然我要为当前可行的 SQL Server 2005/2008 响应投赞成票,但我不能恐怕不接受他们。
【问题讨论】:
第二个表是否有任何属性说明哪一行是第一秒等。 @Cybernate 没有,除了索引的顺序 【参考方案1】:假设Carriers
有一个名为PRIMARY KEY
的id
:
SELECT i.[Invoice Number],
c.[Carrier Name]
FROM Invoice i
JOIN Carriers c
ON c.id =
(
SELECT TOP 1 ID
FROM Carriers ci
WHERE ci.InvoiceKey = i.InvoiceKey
ORDER BY
id -- or whatever
)
【讨论】:
与 group by 和 having 子句相比,this 的性能可能很差。您正在针对 Invoice 中的每一行的 Carriers 表执行相关子查询。 @Chris:与什么相比?你没有提供一个有效的例子。 @Quassnoi 太棒了,太棒了,太棒了,太完美了,救了我的命。【参考方案2】:这对我有用:
select ir.[Invoice Number], c.[Carrier Name]
from
(select ROW_NUMBER() over (order by i.[Invoice Number] asc) AS RowNumber, i.[Invoice Number], i.InvoiceKey
from Invoice i) AS ir
left join Carriers c
on ir.InvoiceKey = c.InvoiceKey
where RowNumber = 1
union all
select ir.[Invoice Number], NULL as [Carrier Name]
from
(select ROW_NUMBER() over (order by i.[Invoice Number] asc) AS RowNumber, i.[Invoice Number]
from Invoice i) AS ir
where RowNumber > 1
或
select TOP 1 i.[Invoice Number], c.[Carrier Name]
from Invoice i
left join Carriers c
on i.InvoiceKey = c.InvoiceKey
union all
select ir.[Invoice Number], NULL as [Carrier Name]
from
(select ROW_NUMBER() over (order by i.[Invoice Number] asc) AS RowNumber, i.[Invoice Number]
from Invoice i) AS ir
where RowNumber > 1
【讨论】:
+1 如果我在 SQL Server 2005+ 上就可以完成这项工作,但我忘了提到我需要在 SQL Server 2000 机器上运行。【参考方案3】:我就是这样做的,使用的语法与你的稍有不同(mysql 风格),但我想你也可以将它应用到你的解决方案中:
SELECT i.invoiceNumber, c.carrierName
FROM Invoice as i
LEFT JOIN Carriers as c ON (c.id = (SELECT id FROM Carriers WHERE invoiceKey = i.invoiceKey ORDER BY id LIMIT 1))
这将从 Invoice 中获取所有记录,并将其与 Carriers 中的一个(或零个)记录连接起来,特别是具有相同 invoiceKey 且只有第一个的记录。
只要您在 Carriers.invoiceKey 上有一个索引,这个查询的性能应该是可以接受的。
塞巴斯蒂安
【讨论】:
【参考方案4】:;with cteRowNumber as (
select c.InvoiceKey, c.[Carrier Name], ROW_NUMBER() over (partition by c.InvoiceKey order by c.[Carrier Name]) as RowNum
from Carriers c
)
select i.[Invoice Number],
rn.[Carrier Name]
from Invoice i
left join cteRowNumber rn
on i.InvoiceKey = rn.InvoiceKey
and rn.RowNum = 1
【讨论】:
@abatishchev:我没看到。如果我将rn.RowNum = 1
包含在我的连接条件中,那应该只连接“第一个”(由窗口函数的顺序定义)。
+1 技术上正确,但我忘了提到我正在寻找 SQL Server 2000,所以 CTE 不是一个选项。【参考方案5】:
在这种情况下,我经常使用一种设备,我在此将其应用于您的示例并在下面进行描述:
SELECT
i.[Invoice Number],
c.[Carrier Name]
FROM Invoice i
INNER JOIN Carriers c ON i.InvoiceKey = c.InvoiceKey
INNER JOIN (
SELECT MIN(ID) AS ID
FROM Carriers
GROUP BY InvoiceKey
) c_top ON c.ID = c_top.ID
我认为,这大致就是 Quassnoi 发布的内容,只是我尽量避免使用这样的 SELECT TOP。
Invoice
与Carriers
基于它们的链接表达式(在本例中为InvoiceKey
)连接。现在,Carriers
可以有多个相同的InvoiceKey
行,所以我们需要限制输出。这是使用派生表完成的。
派生表根据用于链接两个表 (InvoiceKey
) 的相同表达式对 Carrier 中的行进行分组。
还有另一种方法:您可以使用IN (subquery)
而不是加入派生表,效果相同。也就是说,完整的查询将如下所示:
SELECT
i.[Invoice Number],
c.[Carrier Name]
FROM Invoice i
INNER JOIN Carriers c ON i.InvoiceKey = c.InvoiceKey
AND c.ID IN (SELECT MIN(ID) FROM Carriers GROUP BY InvoiceKey)
【讨论】:
【参考方案6】:group by carriername having max(invoicenumber)
获取每张发票的第一个承运人:
group by invoicenumber having max(carriername)
-- substitute the column you want to order by for carrier name to change which is 'first'
【讨论】:
这不起作用 - 这只会给我发票编号最高的承运人的承运人名称。我需要的是每张发票的第一个承运人。 不,这应该为您提供每个承运人的最高发票编号。通过撤销分组依据/拥有,您可以获得每张发票的第一个承运人。 请记住,在 group by 子句之后应用有子句:-)HAVING MAX(invoicenumber)
甚至不会解析。 MAX(invoicenumber)
不是谓词。
@Chris:这也不会解析。您不能在HAVING
子句中使用未聚合的表达式。【参考方案7】:
您也可以使用OUTER APPLY
。
请注意对未知字段名称使用尖括号:
Select i.[Invoice Number], c.[Carrier Name], x.<Carrier_field1>
From Invoice i
OUTER APPLY
(
SELECT TOP 1
FROM Carriers c
WHERE c.[InvoiceKey] = i.[InvoiceKey]
ORDER BY <order _clause>
) x
【讨论】:
以上是关于如何加入子查询的第一行?的主要内容,如果未能解决你的问题,请参考以下文章
sql 通过返回子查询的第一行来获取员工的最新活动配置文件,该子查询将获取所提供的e的所有员工配置文件