Dou*_*las 3 sql t-sql sql-server row-number dense-rank
想象一下,我有一个非规范化的表,如下所示:
CREATE TABLE Persons
(
Id int identity primary key,
FirstName nvarchar(100),
CountryName nvarchar(100)
)
INSERT INTO Persons
VALUES ('Mark', 'Germany'),
('Chris', 'France'),
('Grace', 'Italy'),
('Antonio', 'Italy'),
('Francis', 'France'),
('Amanda', 'Italy');
Run Code Online (Sandbox Code Playgroud)
我需要构建一个返回每个人姓名的查询,以及他们所在国家/地区的唯一ID.ID不一定必须是连续的; 更重要的是,他们并没有必须在任何顺序.实现这一目标的最有效方法是什么?
最简单的解决方案似乎是DENSE_RANK
:
SELECT FirstName,
CountryName,
DENSE_RANK() OVER (ORDER BY CountryName) AS CountryId
FROM Persons
-- FirstName CountryName CountryId
-- Chris France 1
-- Francis France 1
-- Mark Germany 2
-- Amanda Italy 3
-- Grace Italy 3
-- Antonio Italy 3
Run Code Online (Sandbox Code Playgroud)
然而,这在我的CountryName
专栏中引起了一种分类,这是一种浪费的性能损失.我提出了这个替代方案,它使用ROW_NUMBER
了众所周知的技巧来抑制它的排序:
SELECT P.FirstName,
P.CountryName,
C.CountryId
FROM Persons P
JOIN (
SELECT CountryName,
ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS CountryId
FROM Persons
GROUP BY CountryName
) C
ON C.CountryName = P.CountryName
-- FirstName CountryName CountryId
-- Mark Germany 2
-- Chris France 1
-- Grace Italy 3
-- Antonio Italy 3
-- Francis France 1
-- Amanda Italy 3
Run Code Online (Sandbox Code Playgroud)
假设第二个查询在一般情况下表现更好(不仅仅是在我设计的数据集上),我是否正确?有CountryName
哪些因素可能会有所不同(例如索引)?是否有更优雅的表达方式?
为什么你认为聚合比窗口函数便宜?我问,因为我对这两方面都有一些经验,对这个问题没有强烈的意见.如果按下,我猜测窗口函数更快,因为它不必聚合所有数据,然后将结果重新加入.
这两个查询将具有非常不同的执行路径.看哪个表现更好的正确方法是尝试一下.对环境中足够大的数据样本运行两个查询.
顺便说一下,我认为没有正确的答案,因为性能取决于几个因素:
如果您担心性能,只想要一个唯一的号码,您可以考虑使用checksum()
.这确实存在碰撞风险.对于200个左右的国家来说,这种风险非常非常小.另外,如果确实发生了,您可以对其进行测试并对其进行一些处理.查询将是:
SELECT FirstName, CountryName, CheckSum(CountryName) AS CountryId
FROM Persons;
Run Code Online (Sandbox Code Playgroud)