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
) с точки зрения других логических операторов, но есть два сильных кандидата:
-
(if p then q) and (if not p then r)
,(p ⇒ q) ∧ (¬p ⇒ q)
-
(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))
двойной, может быть удален