Tại sao không phải là chữ số THÍCH [0-9]?


13

Đối chiếu mặc định của máy chủ của tôi là Latin1_General_CI_AS, như được xác định bởi truy vấn này:

SELECT SERVERPROPERTY('Collation') AS Collation;

Tôi đã rất ngạc nhiên khi phát hiện ra rằng với sự đối chiếu này, tôi có thể ghép các ký tự không có chữ số trong chuỗi bằng cách sử dụng vị ngữ LIKE '[0-9]'.

Tại sao trong đối chiếu mặc định điều này xảy ra? Tôi không thể nghĩ về một trường hợp mà điều này sẽ hữu ích. Tôi biết tôi có thể làm việc xung quanh hành vi bằng cách sử dụng đối chiếu nhị phân, nhưng có vẻ như là một cách kỳ lạ để thực hiện đối chiếu mặc định.

Các chữ số lọc tạo ra các ký tự không phải là chữ số

Tôi có thể chứng minh hành vi bằng cách tạo một cột chứa tất cả các giá trị ký tự một byte có thể và lọc các giá trị bằng biến vị ngữ khớp chữ số.

Câu lệnh sau đây tạo một bảng tạm thời với 256 hàng, một hàng cho mỗi điểm mã trong trang mã hiện tại:

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;

Mỗi hàng chứa giá trị nguyên của điểm mã và giá trị ký tự của điểm mã. Không phải tất cả các giá trị ký tự đều có thể hiển thị - một số điểm mã là các ký tự được kiểm soát chặt chẽ. Đây là một mẫu chọn lọc của đầu ra của 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 ÿ

Tôi hy vọng có thể lọc trên cột Biểu tượng để tìm các ký tự chữ số bằng cách sử dụng một vị từ THÍCH và chỉ định phạm vi của các ký tự '0' thông qua '9':

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

Nó tạo ra một đầu ra đáng ngạc nhiên:

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 ¾

Tập hợp các điểm mã 48 đến 57 là những điểm tôi mong đợi. Điều làm tôi ngạc nhiên là các biểu tượng cho siêu ký tự và phân số cũng được bao gồm trong tập kết quả!

Có thể có một lý do toán học để nghĩ về số mũ và phân số là số, nhưng có vẻ sai khi gọi chúng là chữ số.

Sử dụng đối chiếu nhị phân như một cách giải quyết

Tôi hiểu rằng để có được kết quả mà tôi mong đợi, tôi có thể buộc đối chiếu nhị phân tương ứng Latin1_General_BIN:

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

Tập kết quả chỉ bao gồm các điểm mã 48 đến 57:

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

Câu trả lời:


22

[0-9] không phải là một số loại biểu thức chính quy được xác định để chỉ khớp các chữ số.

Bất kỳ phạm vi nào trong một LIKEmẫu khớp với các ký tự giữa ký tự bắt đầu và kết thúc theo thứ tự sắp xếp đối chiếu.

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 

Trả về

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

Vì vậy, bạn nhận được các kết quả này bởi vì đối chiếu mặc định của bạn, các ký tự này sắp xếp sau 0nhưng trước 9.

Dường như đối chiếu được xác định để thực sự sắp xếp chúng theo thứ tự toán học với các phân số theo đúng thứ tự giữa 01.

Bạn cũng có thể sử dụng một bộ chứ không phải là một phạm vi. Tránh2 kết hợp, ²bạn sẽ cần CSđối chiếu

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

6

Latin1 là mã trang 1252, trong đó 178 là 'SUPERSCRIPT TWO' . Đây là một siêu ký tự Unicode : là ký tự "2" dưới dạng siêu ký tự . Theo tiêu chuẩn kỹ thuật Unicode # 10 nó nên so sánh bằng 2, xem 8.1 Collation Folding :

Tương thích bản đồ (đại học) tương đương, chẳng hạn như các ký tự toàn chiều rộng và siêu ký tự , cho (các) ký tự đại diện

Lỗi sẽ là nếu siêu ký tự 2 sẽ so sánh khác với 2! Trước khi bạn nói 'nhưng cột của tôi không phải là Unicode', hãy yên tâm: theo MSDN (xem Windows Collations), tất cả việc so sánh và sắp xếp chuỗi được thực hiện theo quy tắc Unicode, ngay cả khi biểu diễn trên đĩa là CHAR.

Đối với các ký tự khác trong ví dụ của bạn, thích VULGAR FRACTION ONE QUARTERvà tương tự chúng không so sánh bằng bất kỳ số nào, nhưng, như Mark đã chỉ ra, chúng sắp xếp đúng giữa 0 và 9.

Và, tất nhiên, nếu bạn thay đổi trang mã, bạn sẽ nhận được các kết quả khác nhau. Ví dụ. với Greek_CS_AS( mã trang 1253 ) bạn sẽ nhận được các ký tự có mã 178, 179 và 189.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.