Logic đằng sau ISNUMERIC cho các ký tự đặc biệt là gì?


14

Các ISNUMERICchức năng có một số hành vi bất ngờ. Tài liệu MSDN cho biết:

ISNUMERICtrả về 1 khi biểu thức đầu vào ước tính thành kiểu dữ liệu số hợp lệ; mặt khác, nó trả về 0. Các loại dữ liệu số hợp lệ bao gồm: int, bigint, smallint, tinyint, binary, number, money, smallmoney, float, real .

Và nó cũng có một chú thích:

ISNUMERICtrả về 1 cho một số ký tự không phải là số, chẳng hạn như dấu cộng (+), dấu trừ (-) và ký hiệu tiền tệ hợp lệ, chẳng hạn như ký hiệu đô la ($). Để biết danh sách đầy đủ các ký hiệu tiền tệ, hãy xem tiền và smallmoney (Transact-SQL) .

Được rồi, vì vậy +, -và các biểu tượng tiền tệ được liệt kê dự kiến ​​sẽ được coi là số. Càng xa càng tốt.

Bây giờ cho phần lẻ. Trước tiên, một số ký hiệu tiền tệ từ bài viết được liên kết không phải là số, bao gồm:

  • Ký hiệu tiền tệ Euro, hex 20A0:
  • Dấu hiệu Naira, hex 20A6:
  • Dấu hiệu Rial, hex FDFC:

Điều này thật kỳ lạ và dường như tôi không thể tìm hiểu tại sao? Là phiên bản này hoặc môi trường phụ thuộc?

Tuy nhiên, mọi thứ trở nên kỳ lạ hơn. Dưới đây là một vài điều khác tôi không thể giải thích:

  • /không phải là số, mà \là ( Huh?! )
  • REPLICATE(N'9', 308)là số, nhưng REPLICATE(N'9', 309)không phải là

Câu hỏi đầu tiên và cơ bản nhất là: điều gì giải thích các trường hợp trên? Quan trọng hơn mặc dù: logic đằng sau là gìISNUMERIC , vì vậy tôi có thể tự mình giải thích / dự đoán tất cả các trường hợp?

Đây là một cách tốt để tái tạo mọi thứ:

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;

Khi tôi chạy nó trên hộp Sql Server 2012 cục bộ của tôi, tôi nhận được các kết quả sau:

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

Điều duy nhất có vẻ không chính xác đối với tôi là nó báo cáo sai 0cho năm trong số các giá trị thực sự phù hợp money. Những người khác có vẻ chính xác. FIDDLE SQL
Martin Smith

Mặc dù từ NCHAR(0) - NCHAR(65535)tôi thấy 112 sự khác biệt. Bao gồm các ký tự ₁,₂,₃,4,5,6,7,8,9trông giống số nhưng không thể tạo thành công bất cứ thứ gì cho tôi. Fiddle
Martin Smith

Câu trả lời:


13

Các hành vi chi tiết của ISNUMERICkhông được ghi lại và có lẽ không ai biết đầy đủ nếu không có quyền truy cập mã nguồn. Điều đó nói rằng, có thể là sự giải thích phụ thuộc vào phân loại Unicode (số hay không). Tương tự, các trường hợp kỳ lạ mà bạn đề cập có thể là các lỗi được bảo tồn để tương thích ngược. Vâng, tôi biết rằng nghe có vẻ điên rồ, nhưng nó đã xảy ra.

Vì bạn đang sử dụng SQL Server 2012, nên không cần sử dụng ISNUMERIC. Thay vào đó, hãy sử dụng TRY_CONVERThoặc đồng nghĩa TRY_CASTđể kiểm tra xem một chuỗi có thể chuyển đổi thành một loại nhất định hay không. Trong trường hợp họ cung cấp đầy đủ chức năng, những thứ này thích hợp hơn TRY_PARSE, bởi vì cái sau liên quan đến việc xử lý đắt tiền hơn thông qua tích hợp CLR.


2
Và có lẽ cũng không được nhiều người biết đến với quyền truy cập mã nguồn. :-) Ước gì tôi có thể +1 lần nữa cho đoạn thứ hai. ISNUMERIC () phần lớn là vô dụng vì mục đích của nó là xác định xem một cái gì đó có thể được chuyển đổi thành ít nhất một loại số hay không; rõ ràng điều quan trọng hơn nhiều là bạn có thể chuyển đổi thành một kiểu số cụ thể.
Aaron Bertrand

1
@AaronBertrand Dường như có một số lượng đáng kể các trường hợp mà nó thậm chí không đáp ứng ý định đó.
Martin Smith

9

Dấu gạch chéo ngược ASCII (điểm mã 5C) xảy ra để chia sẻ cùng một điểm mã với ký hiệu yen (¥) trong mã hóa Shift-JIS được sử dụng bởi phiên bản Windows của Nhật Bản và ký hiệu won (₩) trong EUC-KR của Hàn Quốc. Do đó, rất có thể chỉ là sự tiếp nối của chủ đề ký hiệu tiền tệ.


Ah đó là một lý thuyết thú vị. Đó là moneynó diễn viên là tốt.
Martin Smith


3
@Jeroen Tôi không sợ. Chuyển trang mã kế thừa của bản cài đặt Windows của bạn sang tiếng Nhật và bạn nhận được các đường dẫn như C:¥Program Files¥trong explorer.exe
user47620
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.