如何与“row_number() over (partition by [Col] order by [Col])”相反
Posted
技术标签:
【中文标题】如何与“row_number() over (partition by [Col] order by [Col])”相反【英文标题】:How do to the opposite of "row_number() over (partition by [Col] order by [Col])" 【发布时间】:2012-09-23 15:14:44 【问题描述】:我正在尝试合并数据表中的重复条目并给它们一个新编号。
这是一个示例数据集 (runnable copy)
declare @tmpTable table
(ID Varchar(1),
First varchar(4),
Last varchar(5),
Phone varchar(13),
NonKeyField varchar(4))
insert into @tmpTable select 'A', 'John', 'Smith', '(555)555-1234', 'ASDF'
insert into @tmpTable select 'B', 'John', 'Smith', '(555)555-1234', 'GHJK'
insert into @tmpTable select 'C', 'Jane', 'Smith', '(555)555-1234', 'QWER'
insert into @tmpTable select 'D', 'John', 'Smith', '(555)555-1234', 'RTYU'
insert into @tmpTable select 'E', 'Bill', 'Blake', '(555)555-0000', 'BVNM'
insert into @tmpTable select 'F', 'Bill', 'Blake', '(555)555-0000', '%^&*'
insert into @tmpTable select 'G', 'John', 'Smith', '(555)555-1234', '!#RF'
select row_number() over (partition by First, Last, Phone order by ID) NewIDNum, *
from @tmpTable order by ID
现在它给了我结果
NewIDNum ID First Last Phone NonKeyField
-------------------- ---- ----- ----- ------------- -----------
1 A John Smith (555)555-1234 ASDF
2 B John Smith (555)555-1234 GHJK
1 C Jane Smith (555)555-1234 QWER
3 D John Smith (555)555-1234 RTYU
1 E Bill Blake (555)555-0000 BVNM
2 F Bill Blake (555)555-0000 %^&*
4 G John Smith (555)555-1234 !#RF
但这与我想要的相反,NewIDNum
在找到新的密钥组合时会重置其计数器。我希望所有相同的组合都具有相同的 ID。因此,如果它按照我想要的方式运行,我会得到以下结果
NewIDNum ID First Last Phone NonKeyField
-------------------- ---- ----- ----- ------------- -----------
1 A John Smith (555)555-1234 ASDF
1 B John Smith (555)555-1234 GHJK
2 C Jane Smith (555)555-1234 QWER
1 D John Smith (555)555-1234 RTYU
3 E Bill Blake (555)555-0000 BVNM
3 F Bill Blake (555)555-0000 %^&*
1 G John Smith (555)555-1234 !#RF
获得我想要的结果的正确方法是什么?
我没有在原始帖子中包含此要求:如果添加了更多行,我需要 NewIDNum
在此查询的后续运行中为现有行生成相同的数字(假设所有如果对 ID 列进行排序,则新行将具有更高的 ID“值”)
所以如果在以后的日期完成了以下操作
insert into @tmpTable select 'H', 'John', 'Smith', '(555)555-1234', '4321'
insert into @tmpTable select 'I', 'Jake', 'Jons', '(555)555-1234', '1234'
insert into @tmpTable select 'J', 'John', 'Smith', '(555)555-1234', '2345'
再次运行正确的查询会给出
NewIDNum ID First Last Phone NonKeyField
-------------------- ---- ----- ----- ------------- -----------
1 A John Smith (555)555-1234 ASDF
1 B John Smith (555)555-1234 GHJK
2 C Jane Smith (555)555-1234 QWER
1 D John Smith (555)555-1234 RTYU
3 E Bill Blake (555)555-0000 BVNM
3 F Bill Blake (555)555-0000 %^&*
1 G John Smith (555)555-1234 !#RF
1 H John Smith (555)555-1234 4321
4 I Jake Jons (555)555-1234 1234
1 J John Smith (555)555-1234 2345
【问题讨论】:
【参考方案1】:你可以使用dense_rank()
:
dense_rank() over (order by First, Last, Phone) as NewIDNum
作为对您的评论的回应,您可以使用相同的 (First, Last, Phone)
组合对每组行的旧 Id
列的最小值进行排序:
select *
from (
select dense_rank() over (order by min_id) as new_id
, *
from (
select min(id) over (
partition by First, Last, Phone) as min_id
, *
from @tmpTable
) as sub1
) as sub3
order by
new_id
【讨论】:
可以用旧ID订购吗?如果添加更多行,我需要新的 ID 为现有行生成相同的数字(假设在 ID 列上完成order by
时,新行将具有更高的 ID“值”)
更新:添加“Jake Jons”测试会使他的 DR 为 2,而 John 和 Jane Smith 的 DR 都更改为 1。
更新为根据每个(First, Last, Phone)
组中的最低id
分配密集排名。
我发现my own method 使用 group by,但是运行您的查询和我的查询并排运行,您的查询执行时间更快 %。感谢您的帮助!
sub3 是必需的吗?为什么不将最终订单放在 sub3 内并删除最外层(我什至不会在我的实际查询中使用订单(它将提供Insert Into
),但我很好奇)。【参考方案2】:
这应该可以工作
select dense_rank() over (order by First, Last, Phone) NewIDNum, *
from @tmpTable order by ID
【讨论】:
查看我对 Andomar 回答的评论。【参考方案3】:以@Andomar 的原始答案为基础——这将满足您更新后的要求(尽管这不太可能很好地扩展)
select
DENSE_RANK() over (ORDER BY IdRank, First, Last, Phone) AS NewIDNum,
ID,
First,
Last,
Phone,
NonKeyField
from
(
select
MIN(ID) OVER (PARTITION BY First, Last, Phone) as IdRank,
*
from
@tmpTable
) as x
order by
ID;
【讨论】:
【参考方案4】:感谢Andomar's answer作为起点,我自己解决了
select sub1.rn, tt.*
from @tmpTable tt
inner join (
select row_number() over (order by min(ID)) as rn, first, last, phone
from @tmpTable
group by first, last, phone
) as sub1 on tt.first = sub1.first and tt.last = sub1.last and tt.phone = sub1.phone
这会产生
rn ID First Last Phone NonKeyField
-------------------- ---- ----- ----- ------------- -----------
1 A John Smith (555)555-1234 ASDF
1 B John Smith (555)555-1234 GHJK
1 D John Smith (555)555-1234 RTYU
1 G John Smith (555)555-1234 !#RF
1 H John Smith (555)555-1234 4321
1 J John Smith (555)555-1234 2345
2 C Jane Smith (555)555-1234 QWER
3 E Bill Blake (555)555-0000 BVNM
3 F Bill Blake (555)555-0000 %^&*
4 I Jake Jons (555)555-1234 1234
查看 SQL 执行计划,Adnomar 的答案将比我的更大数据集运行得更快。 (53% 的执行时间 VS 47% 的执行时间在彼此相邻运行并选中“包括实际执行计划”时。
【讨论】:
您查询表变量两次,这不如@Andomar 的解决方案最佳。 这解释了我较慢的执行时间。感谢您的洞察力。以上是关于如何与“row_number() over (partition by [Col] order by [Col])”相反的主要内容,如果未能解决你的问题,请参考以下文章