排序以数字结尾的字符串,少数以非数字字符结尾的字符串除外
order strings which end with number with a few exceptions that end with non-numerical character
我正在使用这个脚本:
IF OBJECT_ID('tempdb..#Temp') IS NOT NULL DROP TABLE #Temp
CREATE TABLE #Temp
(
SomeString NVARCHAR(10)
)
INSERT INTO #Temp
SELECT 'X1'
UNION
SELECT 'X10'
UNION
SELECT 'X2'
-- undesired result
SELECT * FROM #Temp ORDER BY SomeString
-- desired result
SELECT * FROM #Temp
ORDER BY LEFT(SomeString,PATINDEX('%[0-9]%',SomeString)-1), -- alphabetical sort
CONVERT(INT,SUBSTRING(SomeString,PATINDEX('%[0-9]%',SomeString),LEN(SomeString))) -- numerical sort
查看更多信息:http://www.essentialsql.com/use-sql-server-to-sort-alphanumeric-values/
要实现这样的 'sensible order'(请参阅声明期望的结果):
X1
X2
X10
不幸的是,如果原始字符串包含任何不以数字结尾的内容(极少数例外),这将不起作用。换句话说,在这种情况下:
INSERT INTO #Temp
SELECT 'X1'
UNION
SELECT 'X10'
UNION
SELECT 'X2'
UNION
SELECT 'X2a'
PS:
一个可能的粗略解决方案:
SELECT * FROM #Temp
ORDER BY LEFT(SomeString,PATINDEX('%[0-9]',CASE WHEN SomeString LIKE '%[a-Z]' THEN SomeString + '0' ELSE SomeString END)-1), -- alphabetical sort
CONVERT(INT,SUBSTRING(SomeString,PATINDEX('%[0-9]',CASE WHEN SomeString LIKE '%[a-Z]' THEN SomeString + '0' ELSE SomeString END),LEN(SomeString)))
假设每个值中唯一的数字构成了您要作为排序依据的数字(与末尾数字之前的数字相反,例如 X5Y10
或 X5Y10A
),那么你最好的选择是 compute/extract 在 INSERT
/ UPDATE
时间(很少见)的数字部分,而不是在 SELECT
时间(通常要多得多)频繁):
创建一个 UDF,dbo.ExtractNumber()
,以提取字符串值的数字部分。虽然 UDF 的性能通常不太理想,但它只会在 INSERT
上执行,如果此字段可更新,则可选择在 UPDATE
上执行。
将非持久化计算列添加到名为 SortField
(或其他)的 table,并定义为:
dbo.ExtractNumber([SomeString])
在该非持久计算列上创建一个非聚集索引,SortField
。
在您的查询中,执行 ORDER BY [SortField]
.
给定示例数据,全部以 X
开头,这意味着字符串的字母部分不相关。如果不是这种情况,并且 alpha 部分 是 首先排序,然后是数字部分(作为真正的数字),那么这将需要两个 UDF 和两个非持久计算列,但仍然只有一个索引。从技术上讲,UDF 可能不需要提取 alpha 部分,因为它可以是一个简单的 SUBSTRING
,从 1 开始,并使用 PATINDEX
找到第一个数字的位置以提供第三个参数SUBSTRING
.
我正在使用这个脚本:
IF OBJECT_ID('tempdb..#Temp') IS NOT NULL DROP TABLE #Temp
CREATE TABLE #Temp
(
SomeString NVARCHAR(10)
)
INSERT INTO #Temp
SELECT 'X1'
UNION
SELECT 'X10'
UNION
SELECT 'X2'
-- undesired result
SELECT * FROM #Temp ORDER BY SomeString
-- desired result
SELECT * FROM #Temp
ORDER BY LEFT(SomeString,PATINDEX('%[0-9]%',SomeString)-1), -- alphabetical sort
CONVERT(INT,SUBSTRING(SomeString,PATINDEX('%[0-9]%',SomeString),LEN(SomeString))) -- numerical sort
查看更多信息:http://www.essentialsql.com/use-sql-server-to-sort-alphanumeric-values/
要实现这样的 'sensible order'(请参阅声明期望的结果):
X1
X2
X10
不幸的是,如果原始字符串包含任何不以数字结尾的内容(极少数例外),这将不起作用。换句话说,在这种情况下:
INSERT INTO #Temp
SELECT 'X1'
UNION
SELECT 'X10'
UNION
SELECT 'X2'
UNION
SELECT 'X2a'
PS:
一个可能的粗略解决方案:
SELECT * FROM #Temp
ORDER BY LEFT(SomeString,PATINDEX('%[0-9]',CASE WHEN SomeString LIKE '%[a-Z]' THEN SomeString + '0' ELSE SomeString END)-1), -- alphabetical sort
CONVERT(INT,SUBSTRING(SomeString,PATINDEX('%[0-9]',CASE WHEN SomeString LIKE '%[a-Z]' THEN SomeString + '0' ELSE SomeString END),LEN(SomeString)))
假设每个值中唯一的数字构成了您要作为排序依据的数字(与末尾数字之前的数字相反,例如 X5Y10
或 X5Y10A
),那么你最好的选择是 compute/extract 在 INSERT
/ UPDATE
时间(很少见)的数字部分,而不是在 SELECT
时间(通常要多得多)频繁):
创建一个 UDF,
dbo.ExtractNumber()
,以提取字符串值的数字部分。虽然 UDF 的性能通常不太理想,但它只会在INSERT
上执行,如果此字段可更新,则可选择在UPDATE
上执行。将非持久化计算列添加到名为
SortField
(或其他)的 table,并定义为:dbo.ExtractNumber([SomeString])
在该非持久计算列上创建一个非聚集索引,
SortField
。在您的查询中,执行
ORDER BY [SortField]
.
给定示例数据,全部以 X
开头,这意味着字符串的字母部分不相关。如果不是这种情况,并且 alpha 部分 是 首先排序,然后是数字部分(作为真正的数字),那么这将需要两个 UDF 和两个非持久计算列,但仍然只有一个索引。从技术上讲,UDF 可能不需要提取 alpha 部分,因为它可以是一个简单的 SUBSTRING
,从 1 开始,并使用 PATINDEX
找到第一个数字的位置以提供第三个参数SUBSTRING
.