Apa logika di balik ISNUMERIC untuk karakter khusus tertentu?

14

The ISNUMERICfungsi memiliki beberapa perilaku yang tidak diharapkan. Dokumentasi MSDN mengatakan:

ISNUMERICmengembalikan 1 ketika ekspresi input mengevaluasi ke tipe data numerik yang valid; selain itu mengembalikan 0. Jenis data numerik yang valid meliputi yang berikut: int, bigint, smallint, tinyint, desimal, numerik, uang, smallmoney, float, real .

Dan itu juga memiliki catatan kaki:

ISNUMERICmengembalikan 1 untuk beberapa karakter yang bukan angka, seperti plus (+), minus (-), dan simbol mata uang yang valid seperti tanda dolar ($). Untuk daftar lengkap simbol mata uang, lihat uang dan uang kecil (Transact-SQL) .

Oke, jadi +,, -dan simbol mata uang yang terdaftar diharapkan dianggap numerik. Sejauh ini baik.

Sekarang untuk bagian yang aneh. Pertama, beberapa simbol mata uang dari artikel yang ditautkan tidak berupa angka, termasuk:

  • Tanda Euro-Mata Uang, hex 20A0:
  • Tanda Naira, hex 20A6:
  • Tanda Rial, hex FDFC:

Ini aneh, dan sepertinya saya tidak tahu mengapa? Apakah versi atau lingkungan ini tergantung?

Namun, hal-hal semakin aneh. Berikut beberapa hal lain yang tidak bisa saya jelaskan:

  • /bukan numerik, tetapi \apakah ( Hah ?! )
  • REPLICATE(N'9', 308)numerik, tetapi REPLICATE(N'9', 309)tidak

Pertanyaan pertama dan paling mendasar adalah: apa yang menjelaskan kasus-kasus di atas? Lebih penting lagi: apa logika di baliknyaISNUMERIC , jadi saya bisa menjelaskan / memprediksi semua kasus sendiri?

Berikut cara yang baik untuk mereproduksi sesuatu:

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;

Ketika saya menjalankan ini pada kotak Sql Server 2012 lokal saya, saya mendapatkan hasil berikut:

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
Jeroen
sumber
Satu-satunya yang tampaknya salah bagi saya adalah bahwa itu melaporkan dengan salah 0lima dari nilai-nilai yang benar-benar berlaku money. Yang lain tampak akurat. SQL FIDDLE
Martin Smith
Meskipun dari NCHAR(0) - NCHAR(65535)saya melihat 112 perbedaan. Termasuk karakter seperti ₁,₂,₃,4,5,6,7,8,9yang terlihat numerik tetapi tidak berhasil melakukan apa pun untuk saya. Fiddle
Martin Smith

Jawaban:

13

Perilaku terperinci dari ISNUMERICtidak didokumentasikan, dan mungkin tidak sepenuhnya diketahui siapa pun tanpa akses kode sumber. Yang mengatakan, mungkin interpretasi itu tergantung pada kategorisasi Unicode (numerik atau tidak). Sama halnya, kasus aneh yang Anda sebutkan mungkin berupa bug yang dipertahankan untuk kompatibilitasnya. Ya saya tahu itu terdengar gila, tetapi itu memang terjadi.

Saat Anda menggunakan SQL Server 2012, tidak perlu digunakan ISNUMERIC. Gunakan TRY_CONVERTatau sinonim TRY_CASTuntuk memeriksa apakah string dapat dikonversi ke jenis yang diberikan. Di mana mereka menyediakan fungsionalitas yang memadai, ini lebih disukai TRY_PARSE, karena yang terakhir melibatkan pemrosesan yang lebih mahal melalui integrasi CLR.

Paul White 9
sumber
2
Dan mungkin tidak sepenuhnya diketahui banyak orang dengan akses kode sumber. :-) Seandainya saya bisa memberi +1 lagi untuk paragraf kedua. ISNUMERIC () sebagian besar tidak berguna karena tujuannya adalah untuk menentukan apakah sesuatu dapat dikonversi ke setidaknya satu tipe numerik; jelas jauh lebih penting untuk mengetahui bahwa Anda dapat mengonversi ke tipe numerik tunggal yang spesifik.
Aaron Bertrand
1
@ AaronBertrand Tampaknya ada cukup banyak kasus yang bahkan tidak memenuhi niat itu.
Martin Smith
9

Garis miring terbalik ASCII (titik kode 5C) terjadi untuk berbagi titik kode yang sama dengan tanda yen (¥) dalam penyandian Shift-JIS yang digunakan oleh Windows versi Jepang, dan tanda menang (₩) dalam EUC-KR Korea. Oleh karena itu, sangat mungkin hanya kelanjutan dari tema tanda mata uang.

pengguna47620
sumber
Ah itu teori yang menarik. Itu moneyyang dilemparkan juga.
Martin Smith
@Jeroen - Ada di Wikipedia FWIW
Martin Smith
3
@ Joen aku takut tidak. Beralih halaman kode lama dari instalasi Windows Anda ke Jepang dan Anda mendapatkan jalur seperti C:¥Program Files¥di explorer.exe
user47620