Подтвердить что ты не робот

Условие условного WH-T-SQL

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

Моя функция имеет параметр @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)

Очевидно, что это неверно.

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

4b9b3361

Ответ 1

Я изменил запрос на использование 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

Ответ 2

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

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

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

(p.LocationTypeId = CASE @IncludeBelow WHEN 0 THEN p.LocationTypeId ELSE @LocationType END)