获取组ID而无需排序的有效方法

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哪些因素可能会有所不同(例如索引)?是否有更优雅的表达方式?

Gor*_*off 5

为什么你认为聚合比窗口函数便宜?我问,因为我对这两方面都有一些经验,对这个问题没有强烈的意见.如果按下,我猜测窗口函数更快,因为它不必聚合所有数据,然后将结果重新加入.

这两个查询将具有非常不同的执行路径.看哪个表现更好的正确方法是尝试一下.对环境中足够大的数据样本运行两个查询.

顺便说一下,我认为没有正确的答案,因为性能取决于几个因素:

  • 哪些列被编入索引?
  • 数据有多大?它适合记忆吗?
  • 有多少个不同的国家?

如果您担心性能,只想要一个唯一的号码,您可以考虑使用checksum().这确实存在碰撞风险.对于200个左右的国家来说,这种风险非常非常小.另外,如果确实发生了,您可以对其进行测试并对其进行一些处理.查询将是:

SELECT FirstName, CountryName, CheckSum(CountryName) AS CountryId
FROM Persons;
Run Code Online (Sandbox Code Playgroud)