TRY_CAST и TRY_PARSE могут возвращать разные результаты
Пересказ статьи Joe Obbish. TRY_CAST and TRY_PARSE Can Return Different Results
Имеется множество руководств, утверждающих, что TRY_CAST является более быстрой и более современной версией TRY_PARSE, и что TRY_PARSE следует использовать только тогда, когда вам необходимо установить необязательный языковой параметр (culture). Однако эти две функции могут возвращать в некоторых случаях различные результаты даже без параметра culture.
Парсинг
Сначала я заброшу миллион строк в единственный столбец столбец типа varchar временной таблицы:
CREATE TABLE #number_as_string (why_tho VARCHAR(100));
INSERT INTO #number_as_string (why_tho)
SELECT ISNULL(CAST(TRY_CAST(q.RN % 33000 AS SMALLINT) AS VARCHAR(100)), '')
FROM
(
SELECT TOP (1000000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) RN
FROM master..spt_values t1
CROSS JOIN master..spt_values t2
) q;
Предположим, что я хочу подсчитать число строк, которые являются числами, подпадающие под ограничения типа данных TINYINT. Следующий код занимает 36937 мс времени процессора на моей машине и возвращает значение 7935:
SELECT COUNT_BIG(*)
FROM #number_as_string
WHERE TRY_PARSE(why_tho AS TINYINT) IS NOT NULL
OPTION (MAXDOP 1);
Плохая производительность запроса не является неожиданной. Документация говорит следующее о TRY_PARSE:
Помните, что имеется определенное падение производительности при парсинге строковых значений.
Кастинг
Я могу использовать TRY_CAST для того, чтобы избежать накладных расходов, связанных с парсингом строк при использовании TRY_PARSE. Следующий код потребляет 110 мс времени процессора, поэтому он значительно быстрей, чем предыдущий запрос:
SELECT COUNT_BIG(*)
FROM #number_as_string
WHERE TRY_CAST(why_tho AS TINYINT) IS NOT NULL
OPTION (MAXDOP 1);
Однако этот запрос возвращает значение 14895, почти удваивая предыдущее число. Результаты запроса отличаются, потому что TRY_PARSE возвращает NULL для пустых строк, в то время как TRY_CAST возвращает 0. Преобразованный запрос (особая благодарность Stephen Morris) возвращает ожидаемое значение 7935 и потребляет 175 мс времени процессора:
SELECT COUNT_BIG(*)
FROM #number_as_string
WHERE TRY_CAST(why_tho AS TINYINT) IS NOT NULL AND why_tho NOT IN ('', '+', '-')
OPTION (MAXDOP 1);
Последние мысли
Дело не в том, что TRY_CAST возвращает неправильные результаты. В SQL Server пустая строка преобразуется в 0, когда проверяется целочисленный тип данных. Более того, когда TRY_CAST применяется к строке, тот, кто пишет запрос, обычно хочет получить только те значения, которые человек воспринял бы как число. TRY_PARSE, видимо, следует набору правил, который больше соответствует человеческому суждению о том, что является числом, а что нет. Во многих случаях все же хорошей идеей является избегать TRY_PARSE по причине падения производительности, однако не забудьте фильтровать пустые строки, если вы не хотите, чтобы они были включены в результирующий набор.
Обратные ссылки
Автор не разрешил комментировать эту запись
Комментарии
Показывать комментарии Как список | Древовидной структурой