Условное предложение T-SQL WHERE - PullRequest
28 голосов
/ 20 декабря 2010

Нашел пару похожих вопросов здесь, но не смог понять, как применить к моему сценарию.

В моей функции есть параметр с именем @ IncludeBelow .Значения 0 или 1 (BIT).

У меня есть этот запрос:

SELECT p.*
FROM Locations l
INNER JOIN Posts p
on l.LocationId = p.LocationId
WHERE l.Condition1 = @Value1
AND   l.SomeOtherCondition = @SomeOtherValue

Если @IncludeBelow равно 0, мне нужно, чтобы запрос был таким:

SELECT p.*
FROM Locations l
INNER JOIN Posts p
on l.LocationId = p.LocationId
WHERE l.Condition1 = @Value1
AND   l.SomeOtherCondition = @SomeOtherValue
AND   p.LocationType = @LocationType -- additional filter to only include level.

Если @IncludeBelow равно 1, эту последнюю строку необходимо исключить.(т.е. не применять фильтр).

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

Вот что я пробовал:

SELECT p.*
FROM Locations l
INNER JOIN Posts p
on l.LocationId = p.LocationId
WHERE l.Condition1 = @Value1
AND   l.SomeOtherCondition = @SomeOtherValue
AND (CASE @IncludeBelow WHEN 0 THEN p.LocationTypeId = @LocationType ELSE 1 = 1)

Очевидно, что это неправильно.

Какой правильный синтаксис?

Ответы [ 3 ]

38 голосов
/ 20 декабря 2010

Я изменил запрос, чтобы использовать EXISTS, потому что, если с POST связано более одного местоположения, будут повторяющиеся записи POST, для которых потребуется условие DISTINCT или GROUP BY, чтобы избавиться от ...

Неразборчивый

Это выполнит наихудшее из возможных решений:

SELECT p.*
  FROM POSTS p
 WHERE EXISTS(SELECT NULL
                FROM LOCATIONS l
               WHERE l.LocationId = p.LocationId
                 AND l.Condition1 = @Value1
                 AND l.SomeOtherCondition = @SomeOtherValue)
   AND (@IncludeBelow = 1 OR p.LocationTypeId = @LocationType)

Саргливая, нединамическая версия

Самоочевидный ....

BEGIN
  IF @IncludeBelow = 0 THEN
    SELECT p.*
      FROM POSTS p
     WHERE EXISTS(SELECT NULL
                    FROM LOCATIONS l
                   WHERE l.LocationId = p.LocationId
                     AND l.Condition1 = @Value1
                     AND l.SomeOtherCondition = @SomeOtherValue)
       AND p.LocationTypeId = @LocationType
  ELSE
    SELECT p.*
      FROM POSTS p
     WHERE EXISTS(SELECT NULL
                    FROM LOCATIONS l
                   WHERE l.LocationId = p.LocationId
                     AND l.Condition1 = @Value1
                     AND l.SomeOtherCondition = @SomeOtherValue) 
END

Унылая, динамическая версия (SQL Server 2005 +):

Любите или ненавидите это, динамический SQL позволяет написать запрос один раз.Просто имейте в виду, что sp_executesql кэширует план запроса, в отличие от EXEC в SQL Server.Настоятельно рекомендуем прочитать Проклятие и благословения динамического SQL , прежде чем рассматривать динамический SQL на SQL Server ...

DECLARE @SQL VARCHAR(MAX)
    SET @SQL = 'SELECT p.*
                  FROM POSTS p
                 WHERE EXISTS(SELECT NULL
                                FROM LOCATIONS l
                               WHERE l.LocationId = p.LocationId
                                 AND l.Condition1 = @Value1
                                 AND l.SomeOtherCondition = @SomeOtherValue)'

    SET @SQL = @SQL + CASE 
                        WHEN @IncludeBelow = 0 THEN
                         ' AND p.LocationTypeId = @LocationType '
                        ELSE ''
                      END   

BEGIN 

  EXEC sp_executesql @SQL, 
                     N'@Value1 INT, @SomeOtherValue VARCHAR(40), @LocationType INT',
                     @Value1, @SomeOtherValue, @LocationType

END
11 голосов
/ 20 декабря 2010

Вы можете записать это как

SELECT  p.*
  FROM  Locations l
INNER JOIN Posts p
    ON  l.LocationId = p.LocationId
  WHERE l.Condition1 = @Value1
    AND l.SomeOtherCondition = @SomeOtherValue
    AND ((@IncludeBelow = 1) OR (p.LocationTypeId = @LocationType))

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

Так как это всего лишь немного, почти наверняка стоит выбрать между двумя блоками SQL с проверкой или без нее,например, используя IF в хранимой процедуре или с различными строками команд в вызывающем коде на основе бита?

3 голосов
/ 23 июля 2014

Вы можете изменить свое заявление CASE на это.Планировщик запросов видит это иначе, но это может быть не более эффективно, чем использование OR:

(p.LocationTypeId = CASE @IncludeBelow WHEN 0 THEN p.LocationTypeId ELSE @LocationType END)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...