为什么SQL显然不合逻辑地对字符串进行排序?

时间:2019-05-07 09:28:59

标签: sql sql-server sorting

今天在使用SQL时,我们发现SQL并没有像我们期望的那样对字符串进行排序。

这是我们的数据集列表:(URL缩短了以防止滥用)

http://10.10.14
http://192.168.
https://m.hanno
https://online.
https://online-
https://owi-000
https://owi2.su
https://owi2-00
https://owi2-71
https://owi-700
https://owi-702
https://owi-703
https://owi-704
https://owi-707
https://owi-708
https://owi-710
https://owi-711
https://owi-712
https://owi-713
https://owi-714
https://owi-715
https://owi-716
https://owi-717
https://owigo.n
https://owigosm
https://owigow.

owi2-URL直接位于owi-000之后。但是,它应该在owi-717之后或owi-000之前,但绝对不能在它们之间。

...
https://owi-000
https://owi2.su
https://owi2-00
https://owi2-71
https://owi-700
...

这就是我们期望的结果。减号在ASCII和许多其他字符编码的2之前。因此,所有带有owi2的网址都应在owi-之后。

...
https://owi-717
https://owi2.su
https://owi2-00
https://owi2-71
https://owigo.n
...

我们在MS Excel中复制了所有URL,令人惊讶的是我们得到了相同的结果。因此,我们检查了字符串以确保没有非打印字符引起该问题。实际上,我们发现没有使用Notepad ++的非打印字符。

我们的问题是:为什么SQL以这种方式对URL进行排序?

2 个答案:

答案 0 :(得分:4)

TLDR : 确保您具有VARCHAR而不是NVARCHAR。 NVARCHAR字符串将忽略连字符。如果您无法更改数据类型,请尝试使用排序规则Latin1_General_CI_AS

就像之前在hereSO herehere这样的地方所提到的,连字符在字母数字字符排序中的使用方式有所不同。

典型归类在排序时会忽略连字符。 引用MSDN文章:

  

用于排序非Unicode数据的SQL归类规则不兼容   与Microsoft Windows提供的任何排序例程   操作系统;但是,Unicode数据的排序是兼容的   使用特定版本的Windows排序规则。因为   当非Unicode和Unicode数据的比较规则不同时,   您使用SQL排序规则时,您可能会看到不同的结果   相同字符的比较,取决于基础数据   类型。例如,如果您使用的是SQL排序规则   “ SQL_Latin1_General_CP1_CI_AS”,非Unicode字符串'a-c'较少   比字符串“ ab”要多,因为连字符(“-”)被单独排序   “ b”之前的字符。但是,如果您转换这些字符串   到Unicode,然后执行相同的比较,即Unicode字符串   N'a-c'被认为大于N'ab',因为Unicode   排序规则使用忽略连字符的“单词排序”。

答案 1 :(得分:1)

或者,如果您确实需要使用nvarchar,则可以使用其他排序规则。您可以更改表中列的排序规则,但这可能会产生不希望的影响,因为如果比较不匹配,则您需要对它们进行任何比较。这将影响可保存性。考虑到您只想订购该产品,则可以简单地使用COLLATE关键字。例如:

SELECT ROW_NUMBER() OVER (ORDER BY URL COLLATE SQL_Latin1_General_CP1_CI_AS),
       ROW_NUMBER() OVER (ORDER BY CONVERT(nvarchar(20),URL) COLLATE SQL_Latin1_General_CP1_CI_AS),
       ROW_NUMBER() OVER (ORDER BY URL COLLATE Latin1_General_CI_AS),
       ROW_NUMBER() OVER (ORDER BY CONVERT(nvarchar(20),URL) COLLATE Latin1_General_CI_AS),
       ROW_NUMBER() OVER (ORDER BY URL COLLATE Latin1_General_100_BIN2),
       ROW_NUMBER() OVER (ORDER BY CONVERT(nvarchar(20),URL) COLLATE Latin1_General_100_BIN2),
       URL
FROM (VALUES('http://10.10.14'),
            ('http://192.168.'),
            ('https://m.hanno'),
            ('https://online.'),
            ('https://online-'),
            ('https://owi-000'),
            ('https://owi2.su'),
            ('https://owi2-00'),
            ('https://owi2-71'),
            ('https://owi-700'),
            ('https://owi-702'),
            ('https://owi-703'),
            ('https://owi-704'),
            ('https://owi-707'),
            ('https://owi-708'),
            ('https://owi-710'),
            ('https://owi-711'),
            ('https://owi-712'),
            ('https://owi-713'),
            ('https://owi-714'),
            ('https://owi-715'),
            ('https://owi-716'),
            ('https://owi-717'),
            ('https://owigo.n'),
            ('https://owigosm'),
            ('https://owigow.'))V(URL)
ORDER BY URL COLLATE Latin1_General_CI_AS;

db<>fiddle

在这里您可以看到排序规则SQL_Latin1_General_CP1_CI_AS对于varcharnvarchar具有不同的顺序。另一方面,Latin1_General_CI_AS对于这两者都是一致的(复制nvarchar与排序规则SQL_Latin1_General_CP1_CI_AS的顺序)。 Latin1_General_100_BIN2也是一致的,但对varchar遵循相同的顺序。