SQL Условный Где

У меня есть хранимая процедура spGetOrders, которая принимает несколько параметров: @startdate и @enddate. Это запрашивает таблицу "Заказы". Один из столбцов в таблице называется "ClosedDate". Этот столбец будет содержать значение NULL, если ордер не был закрыт, или значение даты, если оно было. Я хотел бы добавить параметр @Closed, который будет принимать битовое значение. В простом мире я бы смог сделать..

select * from orders o
where o.orderdate between @startdate AND @enddate
and (if @Closed = 1 then o.ClosedDate IS NULL else o.ClosedDate IS NOT NULL)

Очевидно, что это не сработает.. Я также смотрю на динамический SQL, который является моим последним средством, но начинает выглядеть как ответ..

Пожалуйста помоги..

5 ответов

Решение

Попробуй это:

select * from orders o
where o.orderdate between @startdate AND @enddate
and ((@Closed = 1 And o.ClosedDate IS NULL) Or (@Closed = 0 And o.ClosedDate IS NOT NULL))

Будьте внимательны при смешивании AND и OR в предложении where. При этом ОЧЕНЬ важна скобка для контроля порядка оценки.

Оператор SQL:

SELECT *  
FROM orders  
WHERE orderdate BETWEEN @startdate AND @enddate  
AND (@Closed = 1 OR CLosedDate IS NOT NULL)

Существуют различные подходы к условным условиям формы , каждый из которых отличается удобочитаемостью и производительностью. Все подходы также могут обрабатывать условия формыif-thenиспользуя истинное значение () для ветки else (что логически эквивалентноif p then q else true).

(Примечание: условие, показанное в примере кода вопроса, утверждает, что должно быть NULL, когда оно истинно; это кажется логической ошибкой, поскольку@Closedдолжно быть true, чтобы получить закрытые ордера, и в этом случаеclosedDate не должно быть нулевым, как правильно указано в тексте вопроса. В этом ответеNULLтесты меняются местами, чтобы исправить эту ошибку.)

Функция

В некоторых СУБД есть функция, наиболее близкая к образцу вопроса и концептуально самая простая.

IF(<test>, <then condition>, <else condition>)

SQL Server предоставляетIIF, что эквивалентно выражению (см. ниже). С@Closeв основном содержит логическое значение, его можно проверить напрямую, а не сравнивать с1. В более общем случае будет использоваться выражение сравнения.

IF(@Closed, o.ClosedDate IS NOT NULL, o.ClosedDate IS NULL)

Обратите внимание, что, поскольку здесь используются значения столбцов внутри функции, запросы, использующие обычно, не подлежат анализу .

Оператор

Функция довольно проста: тестовый выбор между двумя альтернативами. выражения являются более общими, допуская произвольное количество альтернатив. Следовательно, у вас есть выбор, какие значения обрабатывать в качестве альтернатив.

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

      -- simple: compare top <expression> to each WHEN <expression>
CASE <expression>
  WHEN <expression> THEN ...
  ...
  ELSE ...
END

-- searched: test each <expression>
CASE 
  WHEN <expression> THEN ...
  ...
  ELSE ...
END

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

      CASE 
  WHEN @Closed THEN o.ClosedDate IS NOT NULL
  ELSE o.ClosedDate IS NULL
END

Если тест более сложен, чем логическая переменная, искомое выражение имеет еще больше смысла. Простая форма может иметь смысл, если тест представляет собой одно значение, с которым вы сравниваете, хотя вам нужно решить, следует ли и как обрабатывать значения, выходящие за рамки того, что вы ожидаете. Вы можете явно перечислить все ожидаемые значения и обрабатывать другие значения как передаваемые:

      CASE @Closed
  WHEN 1 THEN o.ClosedDate IS NOT NULL
  WHEN 0 THEN o.ClosedDate IS NULL
  ELSE 1
END

Вы также можете обработать непредвиденные значения как ошибочные или вызвать ошибку.

Вы можете обрабатывать только одно значение, рассматривая все остальные как альтернативу:

      CASE @Closed
  WHEN 1 THEN o.ClosedDate IS NOT NULL
  ELSE o.ClosedDate IS NULL
END

-- or
CASE @Closed
  WHEN 0 THEN o.ClosedDate IS NULL
  ELSE o.ClosedDate IS NOT NULL
END

Что выбрать, зависит от бизнес-правил.

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

Логические операторы

Логическое следствие,if p then q(в логике также пишетсяp implies q, илиp ⇒ q) логически эквивалентенnot p or q(в логике также пишется¬p ∨ q). Не существует стандартного способа выражения логического троичного выражения ,if p then q else r(в программировании,p ? q : r) с точки зрения других логических операторов, но есть два сильных кандидата:

  1. (if p then q) and (if not p then r),(p ⇒ q) ∧ (¬p ⇒ q)
  2. (if p then q and not r) and (if not p then not q and r) (p ⇒ q∧¬r) ∧ (¬p ⇒ ¬q∧r)

Любой из них может быть основанием для перезаписиif-then-elseс точки зрения других логических операторов:

      -- interpretation 1
    (NOT <test> OR <then condition>) 
AND (    <test> OR <else condition>)

-- interpretation 2
    (NOT <test> OR (    <then condition> AND NOT <else condition>))
AND (    <test> OR (NOT <then condition> AND     <else condition>)

Результат менее читаем, чемIF()или , но потенциально имеет лучшую производительность.

Для рассматриваемого случая, посколькуqиrзависят друг от друга, стоит рассматривать только 1-ю интерпретацию.

      (@Closed OR o.ClosedDate IS NOT NULL) AND (NOT @Closed OR o.ClosedDate IS NULL)

Показ того, что это сводится к соответствующим условиям, остается в качестве упражнения.

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

и Заявления

В некоторых контекстах (например, в хранимых процедурах и функциях) оператор or можно использовать для выбора различных запросов. операторы, как и в отличие от функций, допускают более двух альтернатив (хотя эта функция здесь не нужна).

      IF <test> THEN
  SELECT ... WHERE <then condition> ...
ELSE
  SELECT ... WHERE <else condition> ...
END IF

Оператор отличается от оператора; эти два отличаются:

  • в каких грамматических контекстах они встречаются (примерно, оператор в процедурах, оператор в запросах),
  • тем, что разрешено в ветвях (грубо говоря, оператор разрешает запросы, оператор разрешает выражения) и
  • заявление заканчивается наEND CASE.

АCASEзаявление очень похоже наIFутверждение, особенно в искомой форме:

      CASE
  WHEN <test> THEN
    SELECT ... WHERE <then condition> ...
  ELSE
    SELECT ... WHERE <else condition> ...
END CASE

Эти операторы являются более подробными и включают в себя повторяющийся код (что может привести к ошибкам), но они являются наиболее подходящим вариантом, если производительность является проблемой.

С рассматриваемым образцом:

      IF @Closed THEN
    SELECT *
      FROM Orders o
      WHERE o.OrderDate BETWEEN @startDate AND @endDate
        AND o.ClosedDate IS NOT NULL;
ELSE
    SELECT *
      FROM Orders o
      WHERE o.OrderDate BETWEEN @startDate AND @endDate
        AND o.ClosedDate IS NULL;
END IF;

Или это:

select * from orders o
where o.orderdate between @startdate AND @enddate
and (  (@Closed = 1 AND o.ClosedDate IS NULL)
     OR (ISNULL(@Closed, 0) <> 1 AND o.ClosedDate IS NOT NULL)
     )

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

Удачи!

По существу, выпиши это.

select * from orders o
where o.orderdate between @startdate AND @enddate
and ((@Closed = 1 and o.ClosedDate IS NULL)
    or (@Closed != 1 and o.ClosedDate IS NOT NULL))

двойной, может быть удален

Другие вопросы по тегам