T-SQL Condicional WHERE Cláusula

Encontré un par de preguntas similares aquí sobre esto, pero no pude encontrar la forma de aplicar a mi escenario.

Mi function tiene un parámetro llamado @IncludeBelow . Los valores son 0 o 1 (BIT).

Tengo esta consulta:

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

Si @IncludeBelow es 0, necesito que la consulta sea esta:

 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. 

Si @IncludeBelow es 1, esa última línea debe ser excluida. (es decir, no aplicar filter).

Supongo que debe ser una statement CASE , pero no puede descifrar la syntax.

Esto es lo que he intentado:

 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) 

Obviamente eso no es correcto.

¿Cuál es la syntax correcta?

Cambié la consulta para usar EXISTS porque si hay más de una location asociada con un POST, habría loggings POST duplicates que requerirían una cláusula DISTINCT o GROUP BY para deshacerse de …

El no-sargable

Esto realizará la peor de las soluciones posibles:

 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) 

La versión sargable, no dinámica

Autoexplicativo ….

 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 

La versión sargable y dinámica (SQL Server 2005+):

Lo amo o lo odio, SQL dynamic te permite escribir la consulta una vez. Solo tenga en count que sp_executesql almacena en caching el plan de consulta, a diferencia de EXEC en SQL Server. Recomiendo leer The Curse and Blessings of Dynamic SQL antes de considerar SQL dynamic en 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 

Puedes escribirlo como

 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)) 

que es un patrón que se ve mucho, por ejemplo, para los parameters de búsqueda opcionales. Pero IIRC puede estropear los planes de ejecución de consultas, por lo que puede haber una mejor manera de hacerlo.

Dado que es solo un poco, valdría la pena decidir entre dos bloques de SQL con o sin la verificación, por ejemplo, utilizando un IF en un procedimiento almacenado o con diferentes cadenas de commands en el código de llamada, en function del bit.

Puede cambiar su statement CASE a esto. El planificador de consultas ve esto de manera diferente, pero puede no ser más eficiente que usar OR:

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