Яка логіка ІСНУМЕРИЧНО для певних спеціальних символів?


14

ISNUMERICФункція має деякий несподівану поведінку. Документація MSDN говорить:

ISNUMERICповертає 1, коли вхідне вираження оцінює до чинного числового типу даних; в іншому випадку він повертає 0. До дійсних числових типів даних належать: int, bigint, smallint, tinyint, decimal, numeric, money, smallmoney, float, real .

І він також має виноску:

ISNUMERICповертає 1 для деяких символів, які не є цифрами, такими як плюс (+), мінус (-), і дійсні символи валюти, такі як знак долара ($). Повний список символів валюти див. У грошах та дрібних грошах (Transact-SQL) .

Добре, значить +, -і перераховані символи валюти вважаються числовими. Все йде нормально.

Тепер для непарної частини. По-перше, деякі символи валюти із пов'язаної статті не є числовими, зокрема:

  • Знак євро-валюти, шістнадцятковий 20A0:
  • Знак «Найра», шістнадцятковий 20A6:
  • Знак ріалу, шістнадцятковий 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
Мартін Сміт

Хоча NCHAR(0) - NCHAR(65535)я бачу 112 розбіжностей. У тому числі такі символи, ₁,₂,₃,4,5,6,7,8,9які виглядають числовими, але не вдаються ні до чого. Fiddle
Мартін Сміт

Відповіді:


13

Детальна поведінка ISNUMERICне задокументована і, ймовірно, не повністю відома всім без доступу до вихідного коду. Зважаючи на це, можливо, інтерпретація залежить від категоризації Unicode (чисельної чи ні). Так само дивні випадки, які ви згадуєте, можуть бути помилками, які зберігаються для зворотної сумісності. Так, я знаю, що це звучить божевільно, але це трапляється.

Оскільки ви використовуєте SQL Server 2012, користуватися не потрібно ISNUMERIC. Замість цього скористайтеся TRY_CONVERTабо синонімом, TRY_CASTщоб перевірити, чи перетворюється рядок у певний тип. Там, де вони забезпечують належну функціональність, вони є кращими TRY_PARSE, оскільки останні передбачають більш дорогу обробку за допомогою інтеграції CLR.


2
І, мабуть, теж не дуже відома багатьом людям, які мають доступ до вихідного коду. :-) Бажаю, щоб я міг поставити +1 ще раз для другого пункту. ISNUMERIC () значною мірою марний, оскільки його намір визначити, чи можна щось перетворити принаймні на один числовий тип; очевидно, набагато важливіше знати, що ви можете перетворитись на один, певний числовий тип.
Аарон Бертран

1
@AaronBertrand Начебто існує досить велика кількість випадків, коли він навіть не відповідає цьому наміру.
Мартін Сміт

9

Зворотний косий рядок ASCII (кодова точка 5С) має таку саму кодову точку, що і знак єни (¥) у кодуванні Shift-JIS, що використовується японською версією Windows, і виграний знак (₩) в корейській EUC-KR. Отже, це, швидше за все, просто продовження теми знака валюти.


Ах, це цікава теорія. Це те, moneyщо воно також підпадає.
Мартін Сміт


3
@Jeroen Боюся, що ні. Переключіть застарілу кодову сторінку інсталяції Windows на японську, і ви отримаєте такі шляхи, як C:¥Program Files¥у
explor.exe
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.