ทำไมไม่ใช่ตัวเลข LIKE [0-9]


13

การเปรียบเทียบค่าเริ่มต้นของเซิร์ฟเวอร์ของฉันคือ Latin1_General_CI_AS ตามที่กำหนดโดยแบบสอบถามนี้:

SELECT SERVERPROPERTY('Collation') AS Collation;

ฉันรู้สึกประหลาดใจที่ค้นพบว่าด้วยการเปรียบเทียบนี้ฉันสามารถจับคู่อักขระที่ไม่ใช่ตัวเลขในสตริงโดยใช้เพLIKE '[0-9]'รดิเคต

ทำไมในการจัดเรียงเริ่มต้นนี้เกิดขึ้นได้อย่างไร ฉันไม่สามารถนึกถึงกรณีที่สิ่งนี้จะเป็นประโยชน์ ฉันรู้ว่าฉันสามารถหลีกเลี่ยงพฤติกรรมนี้ได้โดยใช้การเปรียบเทียบแบบไบนารี แต่ดูเหมือนจะเป็นวิธีที่แปลกในการใช้การเปรียบเทียบแบบเริ่มต้น

ตัวกรองหลักสร้าง caracters ที่ไม่ใช่ตัวเลข

ฉันสามารถสาธิตพฤติกรรมโดยการสร้างคอลัมน์ที่มีค่าอักขระไบต์เดียวที่เป็นไปได้ทั้งหมดและกรองค่าด้วยภาคแสดงการจับคู่ตัวเลข

คำสั่งต่อไปนี้สร้างตารางชั่วคราวที่มี 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 predicate และระบุช่วงของอักขระ '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

คำตอบ:


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

เพื่อให้คุณได้รับผลเหล่านี้เพราะภายใต้การเปรียบเทียบค่าเริ่มต้นของการจัดเรียงตัวอักษรเหล่านี้หลังจากที่แต่ก่อน09

ดูเหมือนว่าการเปรียบเทียบถูกกำหนดให้เป็นจริงจัดเรียงไว้ในลำดับที่ทางคณิตศาสตร์ที่มีเศษส่วนในลำดับที่ถูกต้องระหว่างและ01

คุณสามารถใช้ชุดมากกว่าช่วง เพื่อหลีกเลี่ยงการ2จับคู่²คุณจะต้องCSเปรียบเทียบ

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

6

Latin1 เป็นหน้ารหัส 1252 ที่178 คือ 'ยกสอง' นี้เป็น Unicode ยก : เป็นตัวละคร "2" เป็นตัวยก ตามมาตรฐานทางเทคนิค Unicode # 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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.