2012-05-14 5 views
0
где

Вот мой SQLПорядок операций в

SELECT * FROM Answers 
WHERE QuesID = 1 AND OptionID IN (2,5) AND CONVERT(float, ISNULL(AnswerValue, 0)) < 300 

Проблема есть в наследство данные, как строка в OptionID = 6. Когда я запускаю выше запрос дает мне

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

Я подтвердил все данные в QuesID = 1 и OptionId IN (2,5) - все данные с плавающей запятой. Похоже, что SQL выполняет математическую часть этого sql в первую очередь, есть ли способ заставить его проверять OptionID перед запуском сравнения AnswerValue? Или я полностью покинул базу?

ответ

3

Вы не можете контролировать порядок, в котором SQL «разрешит» предложения в вашем предложении WHERE. Я провел некоторое быстрое тестирование, и если было введено строковое значение только для QuesId = 3, оно сработало.

Так что, да, как вы размещены в то время как я пишу это, бросание в

AND isnumeric(AnswerValue) = 1 

будет обрабатывать это, как SQL, кажется, оценить его ранее в процессе.

+3

'ISNUMERIC' не гарантированно работает раньше, чем конверсия, и не гарантирует безопасность. 'ISNUMERIC ('£')' = '1', но он все еще не конвертируется в' float'. –

+0

Даю вам ответ, поскольку он, похоже, работал на меня в описанном мной сценарии. – Wil

1

Понял, я просто добавил ISNUMERIC в SQL и это отсортировано.

+0

Порядок выполнения не определен. Я думал об использовании isnumeric, но не возможно ли выполнить другое ограничение сначала (таким образом, давая ошибку снова)? –

+1

'ISNUMERIC' не гарантированно будет работать раньше, чем конверсия, и не гарантирует вашу безопасность. 'ISNUMERIC ('£')' = '1', но он все еще не конвертируется в' float'. –

1

CASE выражения будет оценивать только его результат выражение (ов), если искомое условие матч, так что вы можете попробовать:

SELECT * FROM Answers 
WHERE 
    CASE WHEN QuesID = 1 AND OptionID IN (2,5) THEN 
     CONVERT(float, ISNULL(AnswerValue, 0)) 
    ELSE 
     500 
    END < 300 

Для более сложных выражений, вы можете использовать вложенные CASE выражение для выполнения более логика - часто, это приведет вас выражать все условия и сделать результат, равный 0 или 1, а затем выполнить окончательное внешнее сравнение с 1:

WHERE 
1 = CASE WHEN <Condition 1> THEN 
     CASE WHEN <Condition 1, Subcondition 1> THEN 0 
      WHEN <Condition 1, Subcondition 2> THEN 1 
     END 
     WHEN <Condition 2> THEN 
     CASE WHEN <Condition 2, Subcondition 1> THEN 1 
      WHEN <Condition 2, Subcondition 2> THEN 0 
     END 
    END 

Где будут оценены два Подусловия для Condition 1 только если Condition 1 верно (и аналогично для Condition 2)

+1

Помните, что порядок операций фактически не гарантируется в выражении 'CASE'.[См. Ответ Аарона здесь для примера.] (Http://dba.stackexchange.com/a/12945/1945) – JNK

+0

@JNK - Ой, противный. Должен быть какой-то способ сделать это. –

+0

Я бы пошел с аргументом case, но 'isnumeric' работал для этого запроса. (И этот оригинальный ответ был опубликован во время моего написания, поэтому я сократил все и не перешел к моим предположениям. Ой.) Полноценный способ сделать это, вероятно, требует нескольких шагов и временных таблиц или, возможно, вложенных подзапросов/, CTE. –

0

Если вы хотите, чтобы гарантировать порядок выполнения операций, и производительность не столь большой заботы, вы могли бы объявить переменную таблицы для хранения результатов ваших первых двух условий, а затем отфильтровать таблицу temp по вашему окончательному условию. например

declare @tempAnswers table (quesID int, optionid int, answervalue varchar(50)) 

insert into @tempAnswers 
select * 
from Answers 
where QuesID = 1 and OptionID in (2,5) 

select * from @tempAnswers 
where convert(float, isnull(AnswerValue, 0)) < 300 
0

Если и когда вы обновляетесь до SQL Server 2012, вы можете использовать TRY_CONVERT. Это собственность, но она решает проблему.

SELECT * FROM Answers 
WHERE QuesID = 1 
AND TRY_CONVERT(int,OptionID) IN (2,5) 
AND CONVERT(float, ISNULL(AnswerValue, 0)) < 300 
Смежные вопросы