switch - Cláusula WHERE condicional de T-SQL
select case sql ejemplos (3)
Cambié la consulta para usar EXISTS porque si hay más de una ubicación asociada con un POST, habría registros POST duplicados que requerirían una cláusula DISTINCT o GROUP BY para deshacerse de ...
El no-sargable
Esto llevará a cabo la peor de las posibles soluciones:
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.
Auto explicativo ....
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 dinámica, sargable (SQL Server 2005+):
Lo ames o lo odies, el SQL dinámico te permite escribir la consulta una vez. Solo tenga en cuenta que sp_executesql almacena en caché el plan de consulta, a diferencia de EXEC en SQL Server. Recomiendo encarecidamente leer la maldición y las bendiciones de SQL dinámico antes de considerar SQL dinámico 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
Encontré un par de preguntas similares aquí sobre esto, pero no pude averiguar cómo aplicar a mi escenario.
Mi función 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 filtro).
Supongo que debe ser una sentencia CASE
, pero no puedo entender la sintaxis.
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 sintaxis correcta?
Puede cambiar su estado de cuenta CASE
a esto. El planificador de consultas ve esto de manera diferente, pero puede que no sea más eficiente que usar OR:
(p.LocationTypeId = CASE @IncludeBelow WHEN 0 THEN p.LocationTypeId ELSE @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 parámetros de búsqueda opcionales. Pero el IIRC puede arruinar los planes de ejecución de consultas, por lo que puede haber una mejor manera de hacerlo.
Dado que es solo un poco, casi vale la pena decidir entre dos bloques de SQL con o sin la verificación, por ejemplo, ¿usar un IF en un procedimiento almacenado o con diferentes cadenas de comandos en el código de llamada, según el bit?