Le problème n'est pas que DISTINCT provoque une dégradation des performances avec les paramètres, c'est que le reste de la requête n'est pas optimisé dans la requête paramétrée car l'optimiseur n'optimisera pas simplement toutes les jointures en utilisant [email protected] _ADMINISTRATOR comme il le fera avec juste 1=1. Il n'optimisera pas les jointures sans distinct car il doit renvoyer des doublons en fonction du résultat des jointures.
Pourquoi? Parce que le plan d'exécution rejetant toutes les jointures serait invalide pour toute valeur autre que @IS_ADMINISTRATOR =1. Il ne générera jamais ce plan, que vous mettiez des plans en cache ou non.
Cela fonctionne aussi bien que la requête non paramétrée sur mon serveur 2008 :
-- PARAMETRIZED QUERY
declare @IS_ADMINISTRATOR int
declare @User_ID int
set @IS_ADMINISTRATOR = 1 -- 1 for administrator 0 for normal
set @User_ID = 50
IF 1 = @IS_ADMINISTRATOR
BEGIN
SELECT DISTINCT -- PLEASE REMEMBER DISTINCT MAKES THE DIFFERENCE!!!
DOC.DOCUMENT_ID
FROM
DOCUMENTS DOC LEFT OUTER JOIN
FOLDERS FOL ON FOL.FOLDER_ID = DOC.FOLDER_ID LEFT OUTER JOIN
ROLES ROL ON (FOL.FOLDER_ID = ROL.FOLDER_ID)
WHERE
1 = 1
END
ELSE
BEGIN
SELECT DISTINCT -- PLEASE REMEMBER DISTINCT MAKES THE DIFFERENCE!!!
DOC.DOCUMENT_ID
FROM
DOCUMENTS DOC LEFT OUTER JOIN
FOLDERS FOL ON FOL.FOLDER_ID = DOC.FOLDER_ID LEFT OUTER JOIN
ROLES ROL ON (FOL.FOLDER_ID = ROL.FOLDER_ID)
WHERE
ROL.USER_ID = @USER_ID
END
Ce qui ressort clairement du plan de requête que je vois exécuter votre exemple, c'est que @IS_ADMINISTRATOR = 1
n'est pas optimisé de la même manière que 1=1
. Dans votre exemple non paramétré, les JOINS sont complètement optimisés et renvoient simplement tous les identifiants de la table DOCUMENTS (très simple).
Il manque également différentes optimisations lorsque @IS_ADMINISTRATOR <> 1
. Par exemple, le LEFT OUTER JOIN
S sont automatiquement remplacés par INNER JOIN
s sans que OR
clause, mais ils sont laissés tels quels avec cette ou cette clause.
Voir aussi cette réponse :SQL LIKE % FOR INTEGERS pour une alternative SQL dynamique.
Bien sûr, cela n'explique pas vraiment la différence de performances dans votre question d'origine, car vous n'avez pas l'OR là-dedans. Je suppose que c'était un oubli.