J'ai trouvé quelques questions similaires ici à ce sujet, mais je n'ai pas pu trouver comment appliquer à mon scénario.
Ma fonction a un paramètre appelé @ IncludeBelow. Les valeurs sont 0 ou 1 (BIT).
J'ai cette requête:
SELECT p.*
FROM Locations l
INNER JOIN Posts p
on l.LocationId = p.LocationId
WHERE l.Condition1 = @Value1
AND l.SomeOtherCondition = @SomeOtherValue
Si @IncludeBelow vaut 0, j'ai besoin que la requête soit la suivante:
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 vaut 1, cette dernière ligne doit être exclue. (c'est-à-dire que vous n'appliquez pas de filtre).
Je suppose que cela doit être une instruction CASE
, mais je ne peux pas comprendre la syntaxe.
Voici ce que j'ai essayé:
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)
Évidemment, ce n'est pas correct.
Quelle est la syntaxe correcte?
J'ai changé la requête pour utiliser EXISTS car s'il y a plus d'un emplacement associé à un POST, il y aurait des enregistrements en double POST qui nécessiteraient une clause DISTINCT ou GROUP BY pour se débarrasser. ..
Cela effectuera la pire des solutions possibles:
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)
Auto-explicatif ....
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
Que vous l'aimiez ou le détestiez, le SQL dynamique vous permet d'écrire la requête une seule fois. Sachez simplement que sp_executesql met en cache le plan de requête, contrairement à EXEC dans SQL Server. Je recommande vivement de lire La malédiction et les bénédictions de Dynamic SQL avant d'envisager le SQL dynamique sur 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
Vous pouvez l'écrire comme
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))
qui est un modèle que vous voyez beaucoup par exemple pour les paramètres de recherche facultatifs. Mais l'IIRC peut perturber les plans d'exécution des requêtes, il peut donc y avoir une meilleure façon de le faire.
Puisqu'il ne s'agit que d'un peu, il pourrait presque être utile de choisir entre deux blocs de SQL avec ou sans vérification, par ex. en utilisant un IF dans une procédure stockée ou avec différentes chaînes de commande dans le code appelant, basé sur le bit?
Vous pouvez changer votre instruction CASE
en ceci. Le planificateur de requêtes voit cela différemment, mais ce n'est peut-être pas plus efficace que d'utiliser OR:
(p.LocationTypeId = CASE @IncludeBelow WHEN 0 THEN p.LocationTypeId ELSE @LocationType END)
Modifiez l'instruction sql comme suit:
SELECT p.*
FROM Locations l
INNER JOIN Posts p
on l.LocationId = p.LocationId
WHERE l.Condition1 = @Value1
AND l.SomeOtherCondition = @SomeOtherValue
AND l.LocationType like @LocationType
La variable @IncludeBelow n'est pas nécessaire
Pour inclure tous les types d'emplacement Définissez @LocationType = '%'
Pour limiter les types d'emplacement renvoyés par la requête Définissez @LocationType = '[Un type d'emplacement spécifique]'
Les instructions Set ci-dessus supposent que la variable @LocationType est un type de données de caractère