如何在 SQL Server 中按字母数字代码排序

Pav*_*vla 2 sql-server-2008 sql-server order-by case

我有一个包含一些项目代码的表格。这些代码是

e.g.: B001, B002, Z15001, Z14001, P003,...
Run Code Online (Sandbox Code Playgroud)

我需要这个结果表:

Z15001
Z15002
...
B001
B002
...
C001
C002
...
Z14099
Z14098
Z14097
...
Z13099
Z13098
...
Z13001
Run Code Online (Sandbox Code Playgroud)

我试过了,但顺序不对。

select *
from table
order by
    case
        when Kod like 'Z15%' then 1
        when Kod like 'B%' then 2
        when Kod like 'C%' then 3
        when Kod like 'D%' then 4
        when Kod like 'E%' then 5 else 6 end asc, Kod asc
    , case when Kod NOT LIKE 'B%' 
        AND Kod NOT LIKE 'C%' 
        AND Kod NOT LIKE 'D%' 
        AND Kod NOT LIKE 'E%' 
        AND Kod NOT LIKE 'Z15%' then 6 
      end desc
Run Code Online (Sandbox Code Playgroud)

我如何在 SQL Server 中执行此操作?

Han*_*non 5

如果您对重复运行这种排序或在更大的数据集上运行感兴趣,您可能需要考虑添加一个关键表来控制排序顺序,并加入该表。

首先,我们设置测试环境:

USE tempdb;
CREATE TABLE dbo.K
(   KOD VARCHAR(255) NOT NULL CONSTRAINT PK_K 
        PRIMARY KEY CLUSTERED
);

CREATE TABLE dbo.KSort
(
    KODSort INT NOT NULL CONSTRAINT PK_KSort
        PRIMARY KEY CLUSTERED
    , KODPrefix VARCHAR(255) NOT NULL 
);


INSERT INTO dbo.K (KOD)
VALUES ('Z15001')
    , ('Z15002')
    , ('B001'  )
    , ('B002'  )
    , ('C001'  )
    , ('C002'  )
    , ('Z14099')
    , ('Z14098')
    , ('Z14097')
    , ('Z13099')
    , ('Z13098')
    , ('Z13001');

INSERT INTO dbo.KSort(KODSort, KODPrefix)
VALUES (10, 'Z15%')
    , (20, 'B%')
    , (30, 'C%')
    , (40, 'Z14%')
    , (50, 'Z13%');

SELECT K.*
FROM dbo.K;
Run Code Online (Sandbox Code Playgroud)

这显示了预期的结果,恰好按聚类键的顺序排序:

在此处输入图片说明

现在,有两个查询,第一个使用标准ORDER BY子句“手动”对结果进行排序。

SELECT *
FROM dbo.K
ORDER BY
    CASE
        WHEN Kod LIKE 'Z15%' THEN 1
        WHEN Kod LIKE 'B%' THEN 2
        WHEN Kod LIKE 'C%' THEN 3
        WHEN Kod LIKE 'Z14%' THEN 4
        WHEN Kod LIKE 'Z13%' THEN 5 
        ELSE 6 
    END ASC;
Run Code Online (Sandbox Code Playgroud)

为此的计划是:

在此处输入图片说明

好看又简单。“排序”操作符占用了计划成本的 77.6%。

如果我们使用 JOIN 来完成我们的排序,就像这样:

SELECT K.*
FROM dbo.K
    INNER JOIN dbo.KSort ON K.KOD LIKE KSort.KODPrefix
ORDER BY KSort.KODSort, K.KOD;
Run Code Online (Sandbox Code Playgroud)

我们从一段更简单的代码开始,它在深层次上吸引了我;但是我们也看到了一个有趣的执行计划:

在此处输入图片说明

我们现在有几个嵌套循环连接,而不是排序运算符。

在处理少量数据时,一旦我们在 SQL Server 的缓冲区中有数据,差异就无关紧要。

但是,让我们看看当我们向KOD表中添加更多行时会发生什么。

TRUNCATE TABLE dbo.K;

INSERT INTO dbo.K(KOD)
SELECT TOP(1000000) CASE ROW_NUMBER() OVER (ORDER BY o.object_id) % 5
   WHEN 0 THEN 'Z15' 
   WHEN 1 THEN 'B' 
   WHEN 2 THEN 'C' 
   WHEN 3 THEN 'Z14' 
   WHEN 4 THEN 'Z13' END
    + CONVERT(VARCHAR(255), ROW_NUMBER() OVER (ORDER BY o.object_id))
FROM sys.objects o, sys.objects o1, sys.columns c;
Run Code Online (Sandbox Code Playgroud)

(以上向表中添加了 1,000,000 行)

在我的机器上,这是一个带有 16GB RAM 的 4 核 Intel I7,我看到以下SET STATISTICS IO,TIME ON;配置:

“标准”排序:

(1000000 row(s) affected)
Table 'K'. Scan count 9, logical reads 2679, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 1094 ms,  elapsed time = 6463 ms.
Run Code Online (Sandbox Code Playgroud)

“加入”:

(1000000 row(s) affected)
Table 'K'. Scan count 5, logical reads 2641, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'KSort'. Scan count 1, logical reads 2, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 437 ms,  elapsed time = 5475 ms.
Run Code Online (Sandbox Code Playgroud)

我在开始每个查询之前使用了 DBCC FREEPROCCACHE,多次运行的结果非常相似。通常,标准排序比 JOIN 版本慢 2 到 3 倍。

除了性能影响之外,使用我建议的键表 ( dbo.KSort) 允许您随意更改输出的排序顺序,而无需修改代码,在我看来,这是一个非常好的奖励。您只需修改KODSort列的值,以便按您喜欢的任何顺序返回结果。例如,以下UPDATE将使Z14*行显示在行之前B*

UPDATE KSort SET KODSort = 15 WHERE KODSort = 40;
Run Code Online (Sandbox Code Playgroud)