Я создаю таблицу #Results и вставляю определенные значения, используя следующий скрипт:

create table #Results
(
    ResOID int identity(1,1),
    FndAbbr varchar(16),
    ResVal varchar(100)
)

insert into #Results
values ('Na', '110'), ('Na', '130'), 
       ('K', '5.02'), ('K', '5.18'), ('K', '3.60'), ('K', '7.00'); 

Теперь, если я выполню этот запрос:

select * 
from #Results
where 
    --(FndAbbr = 'NA' and (convert(int, ResVal) < 120 or convert(int, ResVal) > 160))
    --or
    (FndAbbr = 'K' and (convert(float, ResVal) < 3.0 or convert(float, ResVal) > 6.0))

Или запрос:

select * 
from #Results
where 
    (FndAbbr = 'NA' and (convert(int, ResVal) < 120 or convert(int, ResVal) > 160))
    --or
    --(FndAbbr = 'K' and (convert(float, ResVal) < 3.0 or convert(float, ResVal) > 6.0))

Работает нормально.

Однако, когда я раскомментирую обе строки и выполню запрос:

select * 
from #Results
where 
    (FndAbbr = 'NA' and (convert(int, ResVal) < 120 or convert(int, ResVal) > 160))
    or
    (FndAbbr = 'K' and (convert(float, ResVal) < 3.0 or convert(float, ResVal) > 6.0))

Я получаю ошибку

Ошибка преобразования при преобразовании значения varchar '5.0' в тип данных int

Почему он ведет себя по-другому, когда "или" становится функциональным? Кроме того, какие изменения необходимо внести, чтобы запрос работал?

2
Sye 20 Дек 2019 в 10:16

4 ответа

Эта ошибка из-за типа Reseal столбца, который вы не можете конвертировать с плавающей точкой в Int на сервере SQL попробуй это :

insert into #Results
values ('Na', '110'), ('Na', '130'), 
       ('K', '5'), ('K', '5'), ('K', '3'), ('K', '7');

Он работает правильно.

Вот другой способ, если вы не хотите изменять свои данные в Result, вы можете использовать функцию FLOOR или CEILING для приведения ResVal к int.

select * from #Results
    where 
    (FndAbbr = 'NA' and CEILING(CAST(ResVal as float))<120 )
    or
    (FndAbbr = 'K' and(convert(float,ResVal)<3.0 or convert(float,ResVal)>6.0))
1
mhd.cs 20 Дек 2019 в 10:29

В вашем первом запросе я подозреваю, что SQL Server применяет только convert(int, ResVal) к записям, где FndAbbr = 'NA'. Или, другими словами, я считаю, что записи сначала фильтруются только для тех, где FndAbbr = 'NA', прежде чем проверять второе условие.

В вашем наборе данных ResVal является допустимым целым числом, когда FndAbbr = 'NA', но не когда FndAbbr is 'K' («5.02», «5.18» и т. Д. Являются допустимыми числами с плавающей точкой, но не целыми числами)

В вашем третьем запросе я подозреваю, что включение записей FndAbbr = 'K' приводит к тому, что convert(int, ResVal) также сравнивается с этими записями, что приводит к ошибке.

0
Kei 20 Дек 2019 в 10:29

Вы можете использовать try_convert, как показано ниже. Он возвращает приведение значения к указанному типу данных, если приведение выполнено успешно; иначе ноль.

select * 
from #Results
where 
    (FndAbbr = 'NA' and 
    (try_convert(int, ResVal) < 120 or try_convert(int, ResVal) > 160))
    or
    (FndAbbr = 'K' and 
    (try_convert(float, ResVal) < 3.0 or try_convert(float, ResVal) > 6.0)
    )
0
Deepshikha 20 Дек 2019 в 13:59

SQL Server НЕ гарантирует, что фильтрация в WHERE происходит до вычислений в SELECT. На самом деле SQL - это описательный язык. Запрос SQL описывает набор результатов. Здесь не указан порядок операций.

Это верно и для подзапросов, и для CTE. Движок SQL может свободно переупорядочивать операции - если он отвечает потребностям запроса.

Преобразования могут происходить в разных местах в плане запроса. В частности, SQL Server отправляет преобразования на узлы, которые читают данные. Это считается оптимизацией - это хорошо. Я считаю неправильное обращение с ошибкой ошибкой, хотя Microsoft явно не согласна со мной.

Все поддерживаемые версии SQL Server поддерживают TRY_CONVERT() и TRY_CAST(), что решает эту проблему:

where (FndAbbr = 'NA' and (try_convert(int, ResVal) < 120 or try_convert(int, ResVal) > 160)) or
      (FndAbbr = 'K' and (try_convert(float, ResVal) < 3.0 or try_convert(float, ResVal) > 6.0))

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

НО лучшее решение - хранить данные, используя правильный тип. В этом случае я бы предложил какой-то числовой.

0
Gordon Linoff 20 Дек 2019 в 15:19