在一列上排名表,同时在另一列上排序
Posted
技术标签:
【中文标题】在一列上排名表,同时在另一列上排序【英文标题】:Ranking table on one column whilst sorting on another 【发布时间】:2018-05-03 12:38:07 【问题描述】:我有一个 SQL Server 2008 R2 表的子集,如下所示:
cust_id | prod_id | day | price
--------+---------+-----+-------
137656 194528 42373 9.11
137656 194528 42374 9.11
137656 194528 42375 9.61
137656 194528 42376 9.61
137656 194528 42377 9.11
137656 194528 42378 9.11
我需要像这样对不同的价格周期进行排名:
cust_id | prod_id | day | price | rank
--------+---------+-----+-------+------
137656 194528 42373 9.11 1
137656 194528 42374 9.11 1
137656 194528 42375 9.61 2
137656 194528 42376 9.61 2
137656 194528 42377 9.11 3
137656 194528 42378 9.11 3
以便它按cust_id
、prod_id
和day
升序排序,但在价格变化时增加排名。我曾尝试像这样使用DENSE_RANK()
:
SELECT
cust_id, prod_id, [day], price,
DENSE_RANK() OVER (ORDER BY cust_id, prod_id, price)
FROM
@prices
这会返回类似:
cust_id | prod_id | day | price | rank
--------+---------+-----+-------+------
137656 194528 42373 9.11 1
137656 194528 42374 9.11 1
137656 194528 42377 9.11 1
137656 194528 42378 9.11 1
137656 194528 42375 9.61 2
137656 194528 42376 9.61 2
显然,从排序中排除这一天会给我这些结果,但每当我在DENSE_RANK()
的按部分的顺序中包含这一天时 - 它只是将每个新的一天划分为一个新的 ID....
有人对这应该如何工作有任何想法吗?感谢任何建议,如果需要,可以提供更多信息
【问题讨论】:
作为说明,我尝试使用difference in row number method,但失败了,所以这可能不是一个好方法。 cust_id 或 prod_id 更改时的排名是多少?如果您在示例中也添加它会更好。 【参考方案1】:LAG
和 SUM
的第一个变体
SELECT
*,
1+SUM(IncCount)OVER(PARTITION BY cust_id ORDER BY [day]) [rank]
--1+SUM(IncCount)OVER(PARTITION BY cust_id ORDER BY [day] ROWS BETWEEN unbounded preceding AND current row) [rank]
FROM
(
SELECT
*,
IIF(LAG(price)OVER(PARTITION BY cust_id ORDER BY [day])<>price,1,0) IncCount
--CASE WHEN LAG(price)OVER(PARTITION BY cust_id ORDER BY [day])<>price THEN 1 ELSE 0 END IncCount
FROM Test
) q
没有LAG
的第二个变种
WITH numCTE AS(
SELECT *,ROW_NUMBER()OVER(PARTITION BY cust_id ORDER BY [day]) RowNum
FROM Test
)
SELECT
t1.*,
1+SUM(CASE WHEN t2.price<>t1.price THEN 1 ELSE 0 END)OVER(PARTITION BY t1.cust_id ORDER BY t1.[day]) [rank]
--1+SUM(CASE WHEN t2.price<>t1.price THEN 1 ELSE 0 END)OVER(PARTITION BY t1.cust_id ORDER BY t1.[day] ROWS BETWEEN unbounded preceding AND current row) [rank]
FROM numCTE t1
LEFT JOIN numCTE t2 ON t2.RowNum+1=t1.RowNum AND t2.cust_id=t1.cust_id
递归 CTE 的第三个变体
WITH numCTE AS(
SELECT *,ROW_NUMBER()OVER(PARTITION BY cust_id ORDER BY [day]) RowNum
FROM Test
),
rankCTE AS(
SELECT RowNum,cust_id,prod_id,[day],price,1 [rank]
FROM numCTE
WHERE RowNum=1
UNION ALL
SELECT
n.RowNum,n.cust_id,n.prod_id,n.[day],n.price,
r.[rank]+CASE WHEN n.price<>r.price THEN 1 ELSE 0 END [rank]
FROM numCTE n
JOIN rankCTE r ON n.RowNum=r.RowNum+1 AND n.cust_id=r.cust_id
)
SELECT *
FROM rankCTE
OPTION(MAXRECURSION 0)
【讨论】:
谢谢,它看起来可以工作,但不幸的是 LAG 在 sql 2008 R2 数据库上不起作用,我应该在最初的问题中提到这一点,我现在已经更新了 - 抱歉! 尝试第二种变体。我希望它适用于 SQLServer 2008。 感谢 Leran,我已经测试了后两个选项。选项 2 为新价格的第一行提供了 2,但随后又恢复为 1 - 使得很难按此 ID 分组并获得最小和最大天数来给出价格范围。选项 3 更好,因为它为每个新价格提供了一个递增的排名,但没有考虑不同的 cust_ids 我将 PARTITION BY cust_id 添加到所有三个变体中。 这似乎没有什么不同,但是,我可以对 cust_id 进行分组并进行排名以获得我需要的价格范围 - 所以这非常有效,谢谢!以上是关于在一列上排名表,同时在另一列上排序的主要内容,如果未能解决你的问题,请参考以下文章