Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Wählen Sie die Verschlechterung der Anweisungsleistung aus, wenn Sie DISTINCT mit Parametern verwenden

Das Problem ist nicht, dass DISTINCT eine Leistungsverschlechterung mit Parametern verursacht, sondern dass der Rest der Abfrage in der parametrisierten Abfrage nicht wegoptimiert wird, weil der Optimierer nicht einfach alle Joins mit [email protected] _ADMINISTRATOR wie es mit nur 1=1 sein wird. Es wird die Verknüpfungen ohne nicht wegoptimieren unterschiedlich, weil es Duplikate basierend auf dem Ergebnis der Verknüpfungen zurückgeben muss.

Wieso den? Weil der Ausführungsplan, der alle Joins verwirft, für jeden anderen Wert als @IS_ADMINISTRATOR =1 ungültig wäre. Er wird diesen Plan niemals generieren, unabhängig davon, ob Sie Pläne zwischenspeichern oder nicht.

Dies funktioniert genauso gut wie die nicht parametrisierte Abfrage auf meinem 2008-Server:

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

Aus dem Abfrageplan, den ich beim Ausführen Ihres Beispiels sehe, geht hervor, dass @IS_ADMINISTRATOR = 1 wird nicht wie 1=1 optimiert . In Ihrem nicht parametrisierten Beispiel sind die JOINS vollständig optimiert, und es werden nur alle IDs in der DOCUMENTS-Tabelle zurückgegeben (sehr einfach).

Es fehlen auch verschiedene Optimierungen bei @IS_ADMINISTRATOR <> 1 . Zum Beispiel der LEFT OUTER JOIN S werden automatisch in INNER JOIN geändert s ohne dieses OR -Klausel, aber sie werden unverändert mit belassen that oder Klausel.

Siehe auch diese Antwort:SQL LIKE % FOR INTEGERS für eine dynamische SQL-Alternative.

Dies erklärt natürlich nicht wirklich den Leistungsunterschied in Ihrer ursprünglichen Frage, da Sie das OR nicht darin haben. Ich nehme an, das war ein Versehen.