订购包含数字的字符串

jr1*_*r17 0 sql sql-server

我有一个名为MRvarchar 的列.当我使用ORDER BY它运行查询时似乎没有正确排序.

select MR, LName, FName from users 
  where MR between 'MR20001' and 'MR20002' 
  order by MR
Run Code Online (Sandbox Code Playgroud)

结果:

MR20001   | LINA  | MARY
MR200011  | TEST  | CASE
MR20002   | KO    | MIKE
Run Code Online (Sandbox Code Playgroud)

为什么MR200011以前显示MR20002

Aar*_*and 5

因为它MR是一个字符串,并且 - 例如 - 24排序低于3因为它不关心数值.这就像Smith之前排序Azlea因为z> m.

如果您只想将数字视为数字,则可能不存储MR前缀.根据列名称,这似乎完全是多余的.为什么不单独存储数字部分INT并创建一个追加'MR'运行时的视图?您可以轻松地执行此操作而不会真正影响应用程序(如果您无法通过存储过程控制插入/更新操作,请添加而不是触发器):

CREATE VIEW dbo.users_appended
AS
    SELECT MR = 'MR' + CONVERT(VARCHAR(25), MR), 
        MRSort = MR --, ... other columns ...
      FROM dbo.users;
GO

SELECT MR, other columns
  FROM dbo.users_appended
  ORDER BY MRSort;
Run Code Online (Sandbox Code Playgroud)

如果您无法更改架构,可以说:

ORDER BY CONVERT(BIGINT, SUBSTRING(MR, 3, 25));
Run Code Online (Sandbox Code Playgroud)

但我真的不认为你应该MR存储在那里.如果你不能改变它,那么可以考虑一个视图或计算列来拉出字符串的数字部分.如果您只打算在一个方向上进行排序,您甚至可以索引计算列.

ALTER TABLE dbo.users ADD MRNumber
  AS (CONVERT(BIGINT, SUBSTRING(MR, 3, 25))) PERSISTED;

CREATE INDEX ix_mrnumber ON dbo.users(MRNumber);
Run Code Online (Sandbox Code Playgroud)

您将必须测试维护计算列和索引所需的工作是否通过它对查询的差异来证明.

视图类似,但您不会从索引中获得任何效率:

CREATE VIEW dbo.users_extended
AS
  SELECT MR, ..., MRNumber = CONVERT(BIGINT, SUBSTRING(MR, 3, 25));
GO

SELECT MR, ... 
  FROM dbo.users_extended
  ORDER BY MRNumber;
Run Code Online (Sandbox Code Playgroud)

至于使用LEN,请小心.虽然代码更简单,但它不一定更有效.在我的系统上,我创建了两个具有广泛值的表:

SELECT 'MR'+RTRIM(ABS(object_id)) AS MR 
  INTO dbo.flab 
  FROM sys.all_objects -- 2096 rows

SELECT 'MR'+RTRIM(ABS(s1.object_id)) AS MR 
  INTO dbo.mort 
  FROM sys.all_objects AS s1
  CROSS JOIN sys.all_objects AS s2; -- 4397409 rows
Run Code Online (Sandbox Code Playgroud)

现在,测试这样的简单查询:

SELECT * FROM dbo.flab ORDER BY LEN(MR), MR;
SELECT * FROM dbo.flab ORDER BY CONVERT(BIGINT, SUBSTRING(MR, 3, 25));
SELECT * FROM dbo.mort ORDER BY LEN(MR), MR;
SELECT * FROM dbo.mort ORDER BY CONVERT(BIGINT, SUBSTRING(MR, 3, 25));
Run Code Online (Sandbox Code Playgroud)

在堆上的结果(密切注意持续时间和CPU,尽管SQL Server在估计成本方面吐出无意义):

在此输入图像描述

并使用聚集索引MR:

在此输入图像描述

我还改变了所有计算,BIGINT以避免子字符串超过12个字符的任何潜在危险(并且仍然避免昂贵 - 是的,昂贵的 - LEN()).请注意,估计的成本是50/50,如果INT使用而不是BIGINT(假设使用安全INT- 我认为这是一个安全的假设,因为如果有更大的接受答案会失败,则持续时间差异大致相同值).