我的服务器的默认排序规则是 Latin1_General_CI_AS,由以下查询确定:
SELECT SERVERPROPERTY('Collation') AS Collation;
我惊讶地发现,通过这种排序规则,我可以使用 predicate 匹配字符串中的非数字字符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 ÿ
我希望能够过滤符号列以使用 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
[0-9]
不是某种定义为仅匹配数字的正则表达式。模式中的任何范围都
LIKE
根据排序规则匹配开始和结束字符之间的字符。退货
因此,您会得到这些结果,因为在您的默认排序规则下,这些字符排序在 之后
0
但之前9
。看起来好像排序规则被定义为实际上以数学顺序对它们进行排序,分数在
0
和之间以正确的顺序排列1
。您也可以使用集合而不是范围。为避免
2
匹配²
,您需要一个CS
排序规则Latin1 是代码页 1252,其中178 是 'SUPERSCRIPT TWO'。这是一个Unicode上标:是把字符“2”当作上标。根据Unicode Technical Standard #10,它应该比较等于 2,请参阅8.1 Collation Folding:
如果上标 2 与 2 比较不同,错误将是!在您说“但我的列不是 Unicode”之前,请放心:根据MSDN(请参阅 Windows 排序规则),所有字符串比较和排序都是根据 Unicode 规则完成的,即使磁盘上的表示是 CHAR。
至于您示例中的其他字符,like
VULGAR FRACTION ONE QUARTER
和 like,它们比较不等于任何数字,但是,正如 Mark 已经表明的那样,它们确实在 0 和 9 之间正确排序。当然,如果您更改代码页,您会得到不同的结果。例如。使用
Greek_CS_AS
(代码页 1253),您将获得代码为 178、179 和 189 的字符。