Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

sélectionner la dégradation des performances de l'instruction lors de l'utilisation de DISTINCT avec des paramètres

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.