Problém není v tom, že DISTINCT způsobuje snížení výkonu s parametry, ale v tom, že zbytek dotazu není v parametrizovaném dotazu optimalizován, protože optimalizátor neoptimalizuje pouze všechna spojení pomocí [email protected] _ADMINISTRATOR jako to bude s pouhým 1=1. Neoptimalizuje spojení bez odlišné, protože potřebuje vracet duplikáty na základě výsledku spojení.
Proč? Protože plán provádění vyhazující všechna spojení by byl neplatný pro jakoukoli jinou hodnotu než @IS_ADMINISTRATOR =1. Nikdy tento plán nevygeneruje bez ohledu na to, zda plány ukládáte do mezipaměti nebo ne.
To funguje stejně jako neparametrický dotaz na mém serveru 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
Z plánu dotazů, který vidím při spuštění vašeho příkladu, je zřejmé, že @IS_ADMINISTRATOR = 1
nedojde k optimalizaci stejně jako 1=1
. Ve vašem neparametrizovaném příkladu jsou JOINS zcela optimalizovány a pouze vrací každé ID v tabulce DOCUMENTS (velmi jednoduché).
Při @IS_ADMINISTRATOR <> 1
také chybí různé optimalizace . Například LEFT OUTER JOIN
S se automaticky změní na INNER JOIN
s bez že OR
klauzule, ale jsou ponechány tak, jak jsou s to nebo klauzule.
Viz také tato odpověď:SQL LIKE % FOR INTEGERS pro dynamickou alternativu SQL.
To samozřejmě nevysvětluje rozdíl ve výkonu ve vaší původní otázce, protože tam nemáte OR. Předpokládám, že to bylo nedopatření.