如何选择每组的 TOP 1 记录(分区)
Posted
技术标签:
【中文标题】如何选择每组的 TOP 1 记录(分区)【英文标题】:How to select the TOP 1 record per group (Partition) 【发布时间】:2015-05-15 10:32:53 【问题描述】:我有一个名为 tblRoutes 的表,其中包含一个唯一的 from 和 to 路线列表(f = from;t = to):
| fCity | fState | tCity | tState |
|========|========|========|========|
|New York| NY | Miami | CA |
|Houston | TX |New York| NY |
...
然后我有一个名为 tblCarrierRates 的表格,其中列出了运营商为旅行某些路线提供的一系列等级和费率:
| fCity | fState | tCity | tState | Tier | Rate | CarrID | CarrName |
|========|========|========|========|======|======|========|=============|
|New York| NY | Miami | CA | 2 | $2.99| ABCD | Abracadabra |
|New York| NY | Miami | CA | 1 | $3.00| BUMP | Bumpy Rides |
|Houston | TX |New York| NY | 2 | $4.00| SLOW |Slow Carriers|
|Houston | TX |New York| NY | 2 | $4.01| ABCD | Abracadabra |
...
对于 tblRoutes 中列出的每条独特路线,我正在寻找 tblCarrierRates 提供的 1 条“最佳”路线。
“最佳”的标准是最低Tier,其次是最低Rate。
结果需要返回 tblCarrierRates 中显示的所有字段,因此基于上面 tblRoutes 中显示的 2 条路线,期望的结果是:
| fCity | fState | tCity | tState | Tier | Rate | CarrID | CarrName |
|========|========|========|========|======|======|========|=============|
|New York| NY | Miami | CA | 1 | $3.00| BUMP | Bumpy Rides |
|Houston | TX |New York| NY | 2 | $4.00| SLOW |Slow Carriers|
我正在研究的方法是按升序排序Tier,然后Rate,然后是如何匹配 fCity、fState 的每个唯一组合的 TOP 1 记录、tCity 和 tState:
SELECT A.fCity, A.fState, A.tCity, A.tState, Q.Tier, Q.Rate, Q.CarrID, Q.CarrName
FROM tblRoutes As A LEFT JOIN
(SELECT TOP 1 B.CarrID, B.CarrName, B.fCity, B.fState, B.tCity, B.tState, B.Rate, B.Tier
FROM tblCarrierRates As B
ORDER BY tblCarrierRates.Tier ASC, tblCarrierRates.Rate ASC) As Q
ON (A.tState = Q.tState) AND (A.tCity = Q.tCity) AND (A.fState = Q.fState) AND (A.fCity = Q.fCity);
查询没有失败,但是你可能猜到了,我写的子查询 (Q) 只为 tblRoutes 中的每个路由返回一条记录而不是 1,所以结束结果是:
| fCity | fState | tCity | tState | Tier | Rate | CarrID | CarrName |
|========|========|========|========|======|======|========|=============|
|New York| NY | Miami | CA | 1 | $3.00| BUMP | Bumpy Rides |
|Houston | TX |New York| NY | | | | |
...如您所见,休斯顿到纽约没有任何匹配项,因为我的子查询仅返回 1 个结果,而不是每条路线的 1 个。
我怎样才能达到我想要的结果?
【问题讨论】:
【参考方案1】:我相信您正在寻找 Sql Server 和 Oracle 分析/窗口功能的等价物,例如 ROW_NUMBER() OVER (PARTITION .. ORDER BY)
,例如like so.
虽然这不是在 MS Access 中直接提供的,但我相信可以通过应用相关子查询来模拟 MS Access 中的行编号功能,该子查询计算具有相同“分区”的行数(定义为连接过滤器),其中每一行通过计算同一分区中的前行数进行排名,这些行数“低于”排序标准:
SELECT A.fCity, A.fState, A.tCity, A.tState, Q.Tier, Q.Rate, Q.CarrID, Q.CarrName, TheRank
FROM tblRoutes As A LEFT JOIN
(
SELECT B.CarrID, B.CarrName, B.fCity, B.fState, B.tCity, B.tState, B.Rate, B.Tier,
(
SELECT COUNT(*) + 1
FROM tblCarrierRates rnk
-- Partition Simulation (JOIN)
WHERE B.fCity = rnk.fCity AND B.fState = rnk.fState
AND B.tCity = rnk.tCity AND B.tState = rnk.tState
-- ORDER BY Simulation
AND (rnk.Tier < B.Tier OR
(rnk.Tier = B.Tier AND rnk.Rate < B.Rate))) AS TheRank
FROM tblCarrierRates As B) As Q
ON (A.tState = Q.tState) AND (A.tCity = Q.tCity)
AND (A.fState = Q.fState) AND (A.fCity = Q.fCity)
-- Now, you just want the top rank in each partition.
WHERE TheRank = 1;
请注意性能 - 将为每一行执行子查询。 此外,如果有平局,则将返回两行。
+1 是以行号 1 开始每个分区(因为在其分区中前面的行将为零)
编辑,取出cmets
SELECT A.fCity, A.fState, A.tCity, A.tState, Q.Tier, Q.Rate, Q.CarrID, Q.CarrName, TheRank
FROM tblRoutes As A LEFT JOIN
(
SELECT B.CarrID, B.CarrName, B.fCity, B.fState, B.tCity, B.tState, B.Rate, B.Tier,
(
SELECT COUNT(*) + 1
FROM tblCarrierRates rnk
WHERE B.fCity = rnk.fCity AND B.fState = rnk.fState
AND B.tCity = rnk.tCity AND B.tState = rnk.tState
AND (rnk.Tier < B.Tier OR
(rnk.Tier = B.Tier AND rnk.Rate < B.Rate))) AS TheRank
FROM tblCarrierRates As B) As Q
ON (A.tState = Q.tState) AND (A.tCity = Q.tCity)
AND (A.fState = Q.fState) AND (A.fCity = Q.fCity)
WHERE TheRank = 1
【讨论】:
这看起来像我所追求的,但我收到“FROM 子句中的语法错误”。它突出显示FROM tblCarrierRates As B) As Q
看来 Access 不喜欢我添加的 cmets 并中断了查询。我已经重复了我运行的确切工作查询。
我只得到 1 个结果。我已经取消了WHERE The Rank = 1
,看起来它没有对 fCity、fState、tCity 和 tState 的每个独特组合进行排名
奇怪,我得到了两个,完全按照我发布的 SqlServer 小提琴 - 我使用了插入小提琴中的数据,以及 MS Access 中的上述查询
你是对的,它确实有效。我发布的表格和字段是我在计算机上实际获得的内容的缩写版本,因此我不得不对您的查询进行大量查找和替换。我想我一定没有正确转换它。【参考方案2】:
您的内部查询需要按城市和州分组。这将产生 1 每个城市州,允许外部连接加入这些字段。
独立调试您的内部查询,直到您看到您期望外部查询正常工作的结果。首先取出Top1,这样可以看到排序和分组工作正常。我会明确地将 ASC DESC 放在您的内部查询中,以便其他人知道您希望顶部工作的方向。
【讨论】:
【参考方案3】:您可以尝试以下查询:-
SELECT fCity, fState, tCity, tState, MIN(Tier), MIN(Rate), CarrID, CarrName
FROM tblCarrierRates
GROUP BY fCity, fState, tCity, tState, CarrID, CarrName;
【讨论】:
不,这不会产生预期的结果。 我认为应该。虽然我没有执行它,但它应该是。 是的,2 个MIN
s 行不通,因为他们只专注于自己的领域
@GordThompson 我正在查看您在其他地方提供的关于排名的答案...我正在尝试应用它,但我无法表达我的最低等级和最低利率效果之后:A.LaneTier >= B.LaneTier AND A.Rate >= B.Rate
***.com/questions/24405074/…以上是关于如何选择每组的 TOP 1 记录(分区)的主要内容,如果未能解决你的问题,请参考以下文章