Оценивает ли оператор SQL Server CASE все условия или завершает работу при первом ИСТИННОМ условии?

44

CASEОценивает ли оператор SQL Server (в частности, 2008 или 2012) все WHENусловия или он завершается, когда находит WHENпредложение, которое оценивается как истинное? Если он проходит через весь набор условий, означает ли это, что последнее условие, оцениваемое как истинное, перезаписывает то, что выполнил первое условие, оцениваемое как истинное? Например:

SELECT
    CASE
        WHEN 1+1 = 2 THEN'YES'
        WHEN 1+1 = 3 THEN 'NO'
        WHEN 1+1 = 2 THEN 'NO' 
    END

Результат - «ДА», хотя последнее условие должно сделать его равным «НЕТ». Кажется, что он выходит, как только находит первое ИСТИННОЕ условие. Может кто-нибудь, пожалуйста, подтвердите, если это так .

Хуан Велес
источник
5
Очень тесно связано: Читает ли SQL Server всю функцию COALESCE, даже если первый аргумент не равен NULL? (как COALESCE()переводится в CASEвыражение.)
ypercubeᵀᴹ

Ответы:

46

• Возвращает result_expression первого выражения input_expression = when_expression, которое оценивается как TRUE .

Ссылка http://msdn.microsoft.com/en-us/library/ms181765.aspx


Это стандартное поведение SQL:

  • CASEВыражение для первого истинного состояния.

  • Если нет истинного условия, оно оценивается как ELSEчасть.

  • Если нет истинного условия и нет ELSEчасти, оно оценивается как NULL.

Джеймс Дженкинс
источник
2
Я просто хотел убедиться, что, если у меня есть 3 случая, которые все оценили бы как истинные, я бы хотел, чтобы выполнялась только задача первой, которая оценивается как истинная, а не 2 других (даже если они также оцениваются как истинная) ). Из примера запроса в моих вопросах это действительно так. Я просто хотел подтвердить. Я также надеялся, что SQL читает условия CASE сверху вниз. Благодарность!
Хуан Велес
15

SQL Server обычно выполняет оценку короткого замыкания для операторов CASE ( SQLFiddle ):

--Does not fail on the divide by zero.
SELECT 
   CASE 
      WHEN 1/1 = 1 THEN 'Case 1'
      WHEN 2/0 = 1 THEN 'Case 2'
   END;

--Fails on the divide by zero.
SELECT 
   CASE 
      WHEN 1/1 = 99 THEN 'Case 1'
      WHEN 2/0 = 99 THEN 'Case 2'
   END;  

Однако существует несколько типов операторов, которые по состоянию на SQL Server 2012 не правильно закорачивают. Смотрите ссылку от ypercube в комментариях.

Oracle всегда выполняет оценку короткого замыкания . См. 11.2 Справочник по языку SQL . Или сравните следующее ( SQLFiddle ):

--Does not fail on the divide by zero.
SELECT
  CASE 
    WHEN 1/1 = 1 THEN 'Case 1'
    WHEN 2/0 = 1 THEN 'Case 2'
  END
FROM dual;


--Fails on the divide by zero.
SELECT
  CASE 
    WHEN 1/1 = 99 THEN 'Case 1'
    WHEN 2/0 = 99 THEN 'Case 2'
  END
FROM dual;

Этот же тест не может быть выполнен с MySQL, потому что он возвращает NULL для деления на ноль. ( SQL Fiddle )

Ли Риффель
источник
Как насчет этого ?: SQL-Fiddle
ypercubeᵀᴹ
1
Скрипка про SQL-сервер . Ответ Аарона здесь: SQL Server читает все функции COALESCE, даже если первый аргумент не равен NULL?
ypercubeᵀᴹ
@ypercube Это действительно интересное поведение. Он оценивает код, который будет выполняться в другой части, но, похоже, игнорирует его в зависимости от того, какие существуют другие выражения WHEN и от того, находится ли деление на ноль внутри MIN или нет. См. Sqlfiddle.com/#!6/d41d8/4468
Ли Риффель
@ypercube Теперь, когда я прочитал некоторые ссылки, которые вы разместили, не могли бы вы сказать, что достаточно предельных случаев, чтобы сказать, что ответ на то, выполняет ли SQL Server оценку короткого замыкания, - обычно?
Ли Риффель
3
Да, я бы согласился на «обычно». Как указывает Аарон на свой ответ, одного теста достаточно, чтобы опровергнуть «случай коротких замыканий всегда». Но обычно это так.
ypercubeᵀᴹ
7

Похоже, что MS SQL Server также использует оценку короткого замыкания.

В следующем тесте у меня есть 3 теста. Первый из них всегда верен, второй дает сбой без ссылки на таблицу, а третий - только при учете данных.
В этом конкретном прогоне обе строки возвращаются успешно. Если я закомментирую первое КОГДА или первое и второе, то я получу неудачи.

CREATE TABLE casetest (test varchar(10))
GO
INSERT INTO casetest VALUES ('12345'),('abcdef')
GO

SELECT CASE WHEN LEN(test)>1 THEN test
        WHEN 1/0 = 1 THEN 'abc'
        WHEN CAST(test AS int) = 1 THEN 'def'
        END
FROM casetest
GO
Кеннет Фишер
источник
1

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

declare @tbl table(id int)
insert into @tbl values(1)
insert into @tbl values(2)
insert into @tbl values(3)

--Fails on the divide by zero.
SELECT * FROM @tbl
where  CASE 
        WHEN id = 2 THEN 1 -- first row in table will not satisfy the condition
        WHEN 2/0 = 1 THEN 1
        ELSE 0
      END =1

-- when filter the records to only who will staisfy the first case when condition, it 
will not fail on the divide by zero
SELECT * FROM @tbl
where ID=2 and -- first row in table will  satisfy the condition
  CASE 
    WHEN id = 2 THEN 1
    WHEN 2/0 = 1 THEN 1
    ELSE 0
  END =1
абдельмонием хафез
источник
1

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

обкрадывать
источник
Вопрос о SQL Server.
Джеймс Андерсон