ตรรกะเบื้องหลัง ISNUMERIC สำหรับอักขระพิเศษบางตัวคืออะไร


14

ISNUMERICฟังก์ชั่นที่มีพฤติกรรมที่ไม่คาดคิด เอกสาร MSDN พูดว่า:

ISNUMERICส่งคืน 1 เมื่อนิพจน์อินพุตประเมินเป็นชนิดข้อมูลตัวเลขที่ถูกต้อง มิฉะนั้นก็จะส่งกลับ 0. ที่ถูกต้องชนิดข้อมูลที่เป็นตัวเลขรวมต่อไปนี้: int, bigint, smallint, TINYINT, ทศนิยม, ตัวเลข, เงิน, smallmoney ลอยจริง

และยังมีเชิงอรรถ:

ISNUMERICส่งคืน 1 สำหรับอักขระบางตัวที่ไม่ใช่ตัวเลขเช่นเครื่องหมายบวก (+), ลบ (-) และสัญลักษณ์สกุลเงินที่ถูกต้องเช่นเครื่องหมายดอลลาร์ ($) สำหรับรายการที่สมบูรณ์ของสัญลักษณ์สกุลเงินดูเงินและ smallmoney (Transact SQL)

เอาล่ะเพื่อให้+, -และสัญลักษณ์สกุลเงินจดทะเบียนที่คาดว่าจะได้รับการพิจารณาเป็นตัวเลข จนถึงตอนนี้ดีมาก

ตอนนี้สำหรับส่วนที่แปลก ก่อนอื่นสัญลักษณ์สกุลเงินบางส่วนจากบทความที่เชื่อมโยงไม่ใช่ตัวเลขรวมไปถึง:

  • เครื่องหมายยูโร - สกุลเงิน hex 20A0:
  • สัญญาณ Naira, hex 20A6:
  • Rial sign, hex FDFC:

นี่เป็นเรื่องแปลกและฉันไม่สามารถหาสาเหตุได้ รุ่นหรือสภาพแวดล้อมนี้ขึ้นอยู่กับ?

อย่างไรก็ตามสิ่งต่าง ๆ ได้รับการแปลก นี่คือบางส่วนที่ฉันไม่สามารถอธิบายได้:

  • /ไม่ใช่ตัวเลข แต่\เป็น ( หือ?! )
  • REPLICATE(N'9', 308)เป็นตัวเลข แต่REPLICATE(N'9', 309)ไม่ใช่

คำถามแรกและคำถามพื้นฐานที่สุดคืออะไรอธิบายถึงกรณีข้างต้น แม้ว่าที่สำคัญกว่า: อะไรคือตรรกะที่อยู่เบื้องหลังISNUMERICดังนั้นฉันสามารถอธิบาย / ทำนายทุกกรณีได้ด้วยตนเอง

นี่เป็นวิธีที่ดีในการทำซ้ำสิ่งต่าง ๆ :

DECLARE @tbl TABLE(txt NVARCHAR(1000));

INSERT INTO @tbl (txt) 
VALUES (N''), (N' '), (N'€'), (N'$'), (N'$$'), 
       (NCHAR(8356)), (NCHAR(8352)), (NCHAR(8358)), (NCHAR(65020)), 
       (N'+'), (N'-'), (N'/'), (N'\'), (N'_'), (N'e'), (N'1e'), (N'e1'), (N'1e1'), 
       (N'1'), (N'-1'), (N'+1'), (N'1+1'), (N''), (N'🄂'), (N'¹'), (N''), (N'½'), 
       (N'🎅'), (REPLICATE(N'9', 307)), (REPLICATE(N'9', 308)), (REPLICATE(N'9', 309)), 
       (REPLICATE(N'9', 310));

SELECT  UNICODE(LEFT(txt, 1)) AS FirstCharAsInt,
        LEN(txt) AS TxtLength,
        txt AS Txt,
        ISNUMERIC(txt) AS [ISNUMERIC]
FROM    @tbl;

เมื่อฉันเรียกใช้สิ่งนี้ในช่อง Sql Server 2012 ในพื้นที่ของฉันฉันจะได้ผลลัพธ์ต่อไปนี้:

FirstCharAsInt   TxtLength   Txt        ISNUMERIC
---------------  ----------  ---------  ----------
NULL             0                      0
32               0                      0
8364             1           €          1
36               1           $          1
36               2           $$         0
8356             1           ₤          1
8352             1           ₠          0  --??
8358             1           ₦          0  --??
65020            1           ﷼‎          0  --??
43               1           +          1
45               1           -          1
47               1           /          0
92               1           \          1  --??
95               1           _          0
101              1           e          0
49               2           1e         0
101              2           e1         0
49               3           1e1        1
49               1           1          1
45               2           -1         1
43               2           +1         1
49               3           1+1        0
9352             1           ⒈         0
55356            2           🄂          0
185              1           ¹          0
9312             1           ①          0
189              1           ½          0
55356            2           🎅         0
57               307        /*...*/     1
57               308        /*...*/     1  --??
57               309        /*...*/     0  --??
57               310        /*...*/     0

สิ่งเดียวที่ดูเหมือนไม่ถูกต้องสำหรับฉันคือมันรายงาน0ค่าห้าค่าที่ผิดเพี้ยนไปจริงmoneyๆ คนอื่น ๆ ดูเหมือนถูกต้อง SQL FIDDLE
Martin Smith

แม้ว่าจากที่NCHAR(0) - NCHAR(65535)ฉันเห็นความขัดแย้ง 112 ข้อ รวมถึงตัวละครต่าง ๆ₁,₂,₃,4,5,6,7,8,9ที่ดูเป็นตัวเลข แต่ไม่ได้ส่งผลอะไรกับฉันเลย Fiddle
Martin Smith

คำตอบ:


13

รายละเอียดพฤติกรรมของISNUMERICไม่ได้บันทึกไว้และอาจไม่เป็นที่รู้จักสำหรับผู้ที่ไม่มีการเข้าถึงซอร์สโค้ด ที่กล่าวมาอาจเป็นไปได้ว่าการตีความขึ้นอยู่กับการจัดประเภท Unicode (ตัวเลขหรือไม่) ในกรณีที่แปลก ๆ ที่คุณพูดถึงอาจจะเป็นข้อผิดพลาดที่เก็บรักษาไว้เพื่อความเข้ากันได้ย้อนหลัง ใช่ฉันรู้ว่ามันฟังดูบ้า แต่มันเกิดขึ้น

ในขณะที่คุณกำลังใช้ SQL Server 2012 ISNUMERICมีความจำเป็นต้องใช้ไม่ได้ ใช้TRY_CONVERTหรือคำพ้องความหมายTRY_CASTแทนเพื่อตรวจสอบว่าสตริงสามารถแปลงเป็นประเภทที่กำหนด ที่ซึ่งพวกเขามีฟังก์ชั่นที่เพียงพอสิ่งเหล่านี้เป็นสิ่งที่ดีกว่าTRY_PARSEเพราะสิ่งหลังนั้นเกี่ยวข้องกับการประมวลผลที่แพงกว่าผ่านการรวม CLR


2
และอาจไม่เป็นที่ทราบแน่ชัดกับผู้คนมากมายที่เข้าถึงซอร์สโค้ดเช่นกัน :-) หวังว่าฉันจะ +1 อีกครั้งในย่อหน้าที่สอง ISNUMERIC () ไม่มีประโยชน์ส่วนใหญ่เพราะความตั้งใจของมันคือการพิจารณาว่าบางสิ่งสามารถแปลงเป็นตัวเลขอย่างน้อยหนึ่งประเภทหรือไม่ เห็นได้ชัดว่าสำคัญกว่าที่คุณจะรู้ว่าคุณสามารถแปลงเป็นตัวเลขที่เป็นประเภทเฉพาะ
Aaron Bertrand

1
@AaronBertrand ดูเหมือนว่าจะมีหลายคดีที่สำคัญซึ่งไม่ถึงกับตั้งใจเช่นนั้น
Martin Smith

9

แบ็กสแลช ASCII (จุดรหัส 5C) เกิดขึ้นเพื่อแชร์จุดรหัสเดียวกันกับเครื่องหมายเยน (¥) ในการเข้ารหัส Shift-JIS ที่ใช้โดย Windows เวอร์ชั่นญี่ปุ่นและเครื่องหมายชนะ (₩) ในเกาหลี EUC-KR ดังนั้นจึงเป็นไปได้มากว่าจะเป็นเพียงความต่อเนื่องของธีมสัญญาณสกุลเงิน


นั่นเป็นทฤษฎีที่น่าสนใจ มันเป็นmoneyสิ่งที่มันได้ปลดเปลื้องไปเช่นกัน
Martin Smith


3
@eroen ฉันไม่กลัว สลับหน้ารหัสดั้งเดิมของการติดตั้ง Windows ของคุณเป็นภาษาญี่ปุ่นและคุณจะได้รับเส้นทางเช่นC:¥Program Files¥ใน explorer.exe
user47620
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.