为什么非数字是 LIKE [0-9]?

Iai*_*der 13 sql-server collation

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

SELECT SERVERPROPERTY('Collation') AS Collation;
Run Code Online (Sandbox Code Playgroud)

我惊讶地发现,通过这种排序规则,我可以使用 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;
Run Code Online (Sandbox Code Playgroud)

每行包含代码点的整数值和代码点的字符值。并非所有字符值都可显示 - 一些代码点是严格控制字符。这是输出的选择性示例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 ÿ
Run Code Online (Sandbox Code Playgroud)

我希望能够过滤 Symbol 列以使用 LIKE 谓词查找数字字符并指定字符范围“0”到“9”:

SELECT CodePoint, Symbol
FROM #CodePage
WHERE Symbol LIKE '[0-9]';
Run Code Online (Sandbox Code Playgroud)

它产生了令人惊讶的输出:

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 ¾
Run Code Online (Sandbox Code Playgroud)

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

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

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

我明白要得到我期望的结果,我可以强制使用相应的二进制排序规则 Latin1_General_BIN:

SELECT CodePoint, Symbol
FROM #CodePage
WHERE Symbol LIKE '[0-9]' COLLATE Latin1_General_BIN;
Run Code Online (Sandbox Code Playgroud)

结果集仅包括代码点 48 到 57:

CodePoint   Symbol
48  0
49  1
50  2
51  3
52  4
53  5
54  6
55  7
56  8
57  9
Run Code Online (Sandbox Code Playgroud)

Mar*_*ith 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 
Run Code Online (Sandbox Code Playgroud)

退货

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
Run Code Online (Sandbox Code Playgroud)

所以你会得到这些结果,因为在你的默认排序规则下,这些字符09.

看起来好像排序规则被定义为实际按数学顺序对它们进行排序,分数在0和之间以正确的顺序排列1

您也可以使用集合而不是范围。为避免2匹配,²您需要CS整理

SELECT CodePoint, Symbol
FROM #CodePage
WHERE Symbol LIKE '[0123456789]' COLLATE Latin1_General_CS_AS
Run Code Online (Sandbox Code Playgroud)


Rem*_*anu 6

Latin1 是代码页 1252,其中178 是 'SUPERSCRIPT TWO'。这是一个 Unicode上标:是字符“2”作为上标。根据Unicode Technical Standard #10它应该比较等于 2,参见8.1 Collat​​ion Folding

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

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

至于您示例中的其他字符,likeVULGAR FRACTION ONE QUARTER和 like 它们不等于任何数字,但是,正如 Mark 已经表明的那样,它们确实在 0 和 9 之间正确排序。

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