我有一个名为MR
的列,它是一个varchar。当我使用ORDER BY
运行查询时,似乎没有正确排序。
select MR, LName, FName from users
where MR between 'MR20001' and 'MR20002'
order by MR
结果:
MR20001 | LINA | MARY
MR200011 | TEST | CASE
MR20002 | KO | MIKE
为什么MR200011
之前显示MR20002
?
答案 0 :(得分: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;
如果您无法更改架构,可以说:
ORDER BY CONVERT(BIGINT, SUBSTRING(MR, 3, 25));
但我真的认为你根本不应该存储MR
。如果你不能改变它,那么可以考虑一个视图或计算列来拉出字符串的数字部分。如果您只打算在一个方向上进行排序,您甚至可以索引计算列。
ALTER TABLE dbo.users ADD MRNumber
AS (CONVERT(BIGINT, SUBSTRING(MR, 3, 25))) PERSISTED;
CREATE INDEX ix_mrnumber ON dbo.users(MRNumber);
您必须测试维护计算列和索引所需的工作是否与查询的差异合理。
视图类似,但您无法从索引中获得任何效率:
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;
至于使用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
现在,测试这样的简单查询:
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));
堆上的结果(密切关注持续时间和CPU,尽管SQL Server在估计成本方面无意义地吐出):
使用MR
上的聚集索引:
我还将所有计算都更改为BIGINT
,以避免子字符串超过12个字符的任何潜在危险(并且仍然避免昂贵 - 是的,昂贵的 - LEN()
)。请注意,如果使用INT
代替BIGINT
,估算费用为50/50且持续时间差异大致相同(假设使用INT
是安全的 - 我认为是一个安全的假设,因为如果有更大的值,接受的答案就会失败。)
答案 1 :(得分:1)
尝试投射,
select MR, LName, FName
from users
where MR between 'MR20001' and 'MR20002'
order by CAST(REPLACE(MR, 'MR', '') AS INT)
答案 2 :(得分:0)
为了避免所有昂贵的铸造和替换,您可以使用它作为替代:
select MR, LName, FName
from Table1
order by LEN(MR),MR