为什么非数字喜欢[0-9]?


13

我的服务器的默认排序规则是Latin1_General_CI_AS,由以下查询确定:

SELECT SERVERPROPERTY('Collation') AS Collation;

我很惊讶地发现,通过这种归类,我可以使用谓词匹配字符串中的非数字字符LIKE '[0-9]'

为什么在默认排序规则中会发生这种情况?我想不出一个有用的例子。我知道我可以使用二进制排序规则来解决此问题,但这似乎是实现默认排序规则的一种奇怪方法。

过滤数字会产生非数字字符

我可以通过创建包含所有可能的单字节字符值的列并使用数字匹配谓词过滤值来演示此行为。

以下语句创建一个临时表,该表具有256行,在当前代码页中的每个代码点对应一个:

WITH P0(_) AS (SELECT 0 UNION ALL SELECT 0),
P1(_) AS (SELECT 0 FROM P0 AS L CROSS JOIN P0 AS R),
P2(_) AS (SELECT 0 FROM P1 AS L CROSS JOIN P1 AS R),
P3(_) AS (SELECT 0 FROM P2 AS L CROSS JOIN P2 AS R),
Tally(Number) AS (
  SELECT -1 + ROW_NUMBER() OVER (ORDER BY (SELECT 0))
  FROM P3
)
SELECT Number AS CodePoint, CHAR(Number) AS Symbol
INTO #CodePage
FROM Tally
WHERE Number >= 0 AND Number <= 255;

每行包含代码点的整数值和代码点的字符值。并非所有的字符值都可以显示-有些代码点严格是控制字符。这是以下输出的选择性样本SELECT CodePoint, Symbol FROM #CodePage

0   
1   
2   
...
32   
33  !
34  "
35  #
...
48  0
49  1
50  2
...
65  A
66  B
67  C
...
253 ý
254 þ
255 ÿ

我希望能够在Symbol列上进行过滤,以使用LIKE谓词并指定字符'0'到'9'的范围来查找数字字符:

SELECT CodePoint, Symbol
FROM #CodePage
WHERE Symbol LIKE '[0-9]';

它产生令人惊讶的输出:

CodePoint   Symbol
48  0
49  1
50  2
51  3
52  4
53  5
54  6
55  7
56  8
57  9
178 ²
179 ³
185 ¹
188 ¼
189 ½
190 ¾

我期望的是代码点集48至57。令我惊讶的是结果集中还包含上标和分数的符号!

将指数和分数视为数字可能是数学上的原因,但是将它们称为数字似乎是错误的。

使用二进制排序规则作为解决方法

我知道要获得预期的结果,我可以强制使用相应的二进制排序规则Latin1_General_BIN:

SELECT CodePoint, Symbol
FROM #CodePage
WHERE Symbol LIKE '[0-9]' COLLATE Latin1_General_BIN;

结果集仅包含代码点48至57:

CodePoint   Symbol
48  0
49  1
50  2
51  3
52  4
53  5
54  6
55  7
56  8
57  9

Answers:


22

[0-9] 不是定义为仅匹配数字的某种正则表达式。

LIKE模式中的任何范围都根据排序规则排序顺序匹配开始字符和结束字符之间的字符。

SELECT CodePoint,
       Symbol,
       RANK() OVER (ORDER BY Symbol COLLATE Latin1_General_CI_AS) AS Rnk
FROM   #CodePage
WHERE  Symbol LIKE '[0-9]' COLLATE Latin1_General_CI_AS
ORDER  BY Symbol COLLATE Latin1_General_CI_AS 

退货

CodePoint            Symbol Rnk
-------------------- ------ --------------------
48                   0      1
188                  ¼      2
189                  ½      3
190                  ¾      4
185                  ¹      5
49                   1      5
50                   2      7
178                  ²      7
179                  ³      9
51                   3      9
52                   4      11
53                   5      12
54                   6      13
55                   7      14
56                   8      15
57                   9      16

之所以得到这些结果,是因为在默认排序规则下,这些字符在0之前但之后排序9

似乎已定义排序规则,以按数学顺序对它们进行实际排序,而分数之间的正确顺序为01

您也可以使用集合而不是范围。为了避免2匹配,²您需要CS排序规则

SELECT CodePoint, Symbol
FROM #CodePage
WHERE Symbol LIKE '[0123456789]' COLLATE Latin1_General_CS_AS

6

Latin1是代码页1252,其中178是'SUPERSCRIPT TWO'。这是Unicode 上标:是字符“ 2”作为上标。根据Unicode技术标准#10,它应比较等于2,请参阅8.1归类折叠

将全角和上标字符等等效性(三级)映射到代表性字符

该错误是如果上标2与2的比较不同!在您说“但我的专栏不是Unicode”之前,请放心:根据MSDN(请参阅Windows排序规则),所有字符串比较和排序都是根据Unicode规则完成的,即使磁盘上的表示形式是CHAR。

至于您示例中的其他字符,like VULGAR FRACTION ONE QUARTER和like之类的字符不等于任何数字,但是,如Mark所示,它们确实在0到9之间正确排序。

而且,当然,如果您要更改代码页,则会得到不同的结果。例如。使用Greek_CS_AS代码页1253),您将获得代码为178、179和189的字符。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.