Ошибка преобразования nvarchar в bigint в предложении WHERE завершается ошибкой, но работает в SELECT

#sql #sql-server #tsql #casting #sql-server-2019

Вопрос:

Я пытаюсь добиться следующего:

Получите все значения полей, где значение FieldValue больше 100, когда значение хранится в виде числа.

Показатель того, сохранялось ли значение в виде числа или нет, определяется типом поля, который является другим предложением where.

Проблема, с которой я сталкиваюсь, заключается в том, что, когда я пытаюсь выполнить преобразование полей данных в моем операторе WHERE, это не удалось.

Я бегу:

 SELECT FieldValue FROM CARData A
JOIN Fields B ON A.FieldId = B.FieldId
WHERE FieldTypeId = 3 AND FieldValue IS NOT NULL 
 

И это возвращает ожидаемый результат:

Результат

Но если бы я добавил предложение WHERE для фильтрации по значению:

 SELECT FieldValue FROM CARData A
JOIN Fields B ON A.FieldId = B.FieldId
WHERE FieldTypeId = 3 AND FieldValue IS NOT NULL 
AND CAST(FieldValue AS BIGINT) > 100
 

Это выбрасывает ошибку:

Ошибка преобразования типа данных nvarchar в bigint.

Я несколько понимаю, в чем проблема — он пытается преобразовать ВСЕ значения в таблице в bigint и терпит неудачу, когда попадает в нечисловое значение.

Я попытался решить эту проблему, вложив первый запрос во второй примерно так:

 SELECT RESULT.FieldValue FROM (
SELECT FieldValue FROM CARData A
JOIn Fields B ON A.FieldId = B.FieldId
WHERE 
FieldTypeId = 3 
AND FieldValue IS NOT NULL 
AND ISNUMERIC(A.FieldValue) = 1) RESULT
WHERE CAST(FieldValue AS BIGINT) > 100
 

Но даже это не возвращает ничего, кроме вышеупомянутой ошибки.

Ответ №1:

Хотя верно, что изменение структуры вашего запроса может привести к тому, что SQL выберет другой план, вам следует ограничить этот метод попытками помочь оптимизатору выбрать эффективный план. Причина этого в том, что если успешное выполнение логики зависит от конкретного выбора плана, то ваш запрос может сработать сегодня, но не сработать завтра (или в процессе работы), когда SQL решит выбрать другой план.

К счастью, вам не нужно полагаться на это здесь! Используйте try_cast

 SELECT FieldValue FROM CARData A
JOIN Fields B ON A.FieldId = B.FieldId
WHERE FieldTypeId = 3 
AND TRY_CAST(FieldValue AS BIGINT) > 100
 

Мне тоже любопытно… является ли это частью курса SQL? Если да, скажите своему преподавателю, чтобы он посетил StackOverflow, чтобы мы могли сказать им, чтобы они прекратили обучать студентов использованию EAV. Если только весь смысл не в том, чтобы показать вам, насколько они ужасны! 🙂

Комментарии:

1. Прежде TRY_CAST чем вы могли бы использовать CASE выражение для принудительного короткого замыкания.

2. Нет, это не учебный курс! Это проблема, которую я рассматриваю в запросе EFCore, поэтому я в конечном итоге перевел ее, переведя SQL-запросы, а затем разбил ее на простой вопрос о том, что на самом деле происходит в запросе; что в конечном итоге выглядит как вопрос курса SQL 😛 Мне, к сожалению, пришлось довольствоваться таблицей EAV, так как данные, хранящиеся здесь, относятся к динамической системе форм. Я, вероятно, мог бы переписать его, чтобы сохранить каждое значение в соответствующем типизированном столбце… В любом случае, спасибо, ваше решение работает!

3. Эй, @AaronBertrand, спасибо за комментарий. Не могли бы вы, пожалуйста, немного подробнее остановиться на этом? Вы предлагаете поместить TRY_CAST в a CASE , который ISNUMERIC сначала проверяет?

4. @Bitz Нет, я говорю, что если бы вы использовали древнюю, неподдерживаемую версию SQL Server до TRY_CAST того, как она существовала, вы могли бы использовать CASE выражение. (И я никогда ничего не говорил об ISNUMERIC этом .)

5. @Bitz Не прикасайтесь ISNUMERIC к столбу баржи, он сломан многими способами ISNUMERIC(-.) , возвращает 1