我需要在SQL中对公寓号码表进行排序。对于那些不熟悉的人来说,这些不一定是数字,但是数字排序需要像它们一样应用。
例如,一组可能的公寓号码将按如下方式订购:
1
10
101
101-A
1000
200
200A
2000
C
D
E-100
F
执行ORDER BY CONVERT( int, ApartmentNumber )
不会起作用,因为并非所有的公寓号都是字符串编码的十进制整数。同样地,ORDER BY ApartmentNumber
无法正常工作,因为它会在101
之后放置1000
。
StackOverflow上的其他QA通常关注已知的固定格式的值Sorting string column containing numbers in SQL?或进行错误处理的排序:Sorting field by numerical value and lexicographical value
在我自己的项目中,以前的开发人员使用了这个技巧:
ORDER BY
RIGHT('00000000000000000000' + RTRIM( ApartmentNumber ), 20 ) ASC
......感觉更糟。事情是:这个技巧在算法上是合理的 - 它只是感觉像是一个黑客必须让数据库引擎执行字符串分配(多次,如果他们不优化串联和RTRIM
到一个字符串操作)。
提供SO的另一种方法是:
ORDER BY
LEN( ApartmentNumber ),
ApartmentNumber
...但是这会产生输入集的错误排序:
1
C
D
F
10
101
200
1000
2000
200A
101-A
E-100
我在本地使用SQL Server 2012(2008年(级别100
)兼容模式),此应用程序将部署到Azure SQL服务器(Azure SQL V12)。
更新:
我一直在考虑一些选择,我认为最好的"是使用固定长度的char(10)
字段而不是varchar(10)
,并确保字段的内容始终保持左对齐,这样就可以通过简单的{{1}确保排序顺序}}
更新2:我意识到上述想法(ORDER BY ApartmentNumber
)没有解决char(10)
需要在200A
之前排序的问题。我认为最好的解决方案是将值规范化为统一的整数表示并将该值存储在数据库中。如果没有用SQL编写,那么进行转换的算法最好(即最简洁)。
答案 0 :(得分:0)
我不认为我使用简单屏蔽模式的原始想法会运行良好,我最终将问题减少到这四个表达式。希望你能在这一切中找到一些价值。
order by
case
when patindex('%[0-9][A-Z-]%', apt) > 0
then cast(substring(apt, 1, patindex('%[0-9][A-Z-]%', apt)) as int)
when patindex('[0-9]%', apt) = 1
then cast(apt as int)
else 99999
end /* numeric_prefix */,
case
when patindex('%[A-Z][0-9-]%', apt) > 0
then left(apt, patindex('%[A-Z][0-9-]%', apt))
when patindex('[A-Z]%', apt) = 1
then apt
else ''
end /* char_prefix */,
case
when patindex('%[A-Z-][0-9]%', apt) > 0
then cast(substring(apt, patindex('%[A-Z-][0-9]%', apt) + 1, 10) as int)
else 0
end /* numeric_suffix */,
replace(apt, '-', '') /* stripped_hyphen */,
case when charindex('-', apt) = 0 then 0 else 1 end /* sort_hyphen_last */
http://rextester.com/MFYVN38156
这是一个可以存储并用于排序的表达式:
case
when patindex('%[0-9][A-Z-]%', apt) > 0
then right('*****' + left(apt, patindex('%[0-9][A-Z-]%', apt)), 5) +
right('>>>>>' +
replace(substring(apt, patindex('%[0-9][A-Z-]%', apt) + 1, 10), '-', ''), 5)
when patindex('%[A-Z][0-9-]%', apt) > 0
then right('>>>>>' + left(apt, patindex('%[A-Z][0-9-]%', apt)), 5) +
right('*****' +
replace(substring(apt, patindex('%[A-Z][0-9-]%', apt) + 1, 10), '-', ''), 5)
when patindex('[0-9]%', apt) = 1 then right('*****' + apt, 5) + '>>>>>'
when patindex('[A-Z]%', apt) = 1 then right('>>>>>' + apt, 5) + '*****'
else '>>>>>*****'
end +
case when charindex('-', apt) = 0 then '' else '-' end /* collate Latin1_General_BIN */
选择 >
作为在数字和字母字符之间进行排序的字符。 *
落在数字之前。输出如下:
apt sort_string
-------- -------------
1 ****1>>>>>
10 ***10>>>>>
101 **101>>>>>
101-A **101>>>>A-
200 **200>>>>>
200A **200>>>>A
200-A **200>>>>A-
1000 *1000>>>>>
2000 *2000>>>>>
C >>>>C*****
D >>>>D*****
E >>>>E*****
E10 >>>>E***10
E100 >>>>E**100
E-100 >>>>E**100-
E101 >>>>E**101
E-101 >>>>E**101-
E200 >>>>E**200
E-200 >>>>E**200-
E1000 >>>>E*1000
E-1001 >>>>E*1001-
F >>>>F*****