выбрать ухудшение производительности статистики при использовании DISTINCT с параметрами - PullRequest
5 голосов
/ 10 декабря 2010

Примечание для вознаграждения - НАЧАЛО:

ПАРАМЕТРЫ СНИФФИНГА (это единственная «идея», о которой сообщалось в вопросах перед наградой), здесь не проблема, как вы можете прочитать в разделе «обновление» в конце вопроса. Проблема действительно связана с тем, как сервер sql создает планы выполнения для параметризованного запроса, когда используется отдельный. Я загрузил очень простую резервную копию базы данных (она работает с SQL Server 2008 R2) здесь (вы должны подождать 20 секунд перед загрузкой). В отношении этой БД вы можете попробовать выполнить следующие запросы:

-- PARAMETRIZED QUERY

declare @IS_ADMINISTRATOR int
declare @User_ID int
set @IS_ADMINISTRATOR = 1 -- 1 for administrator 0 for normal
set @User_ID = 50

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 = @IS_ADMINISTRATOR OR  ROL.USER_ID = @USER_ID

-- NON PARAMETRIZED QUERY

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 OR  ROL.USER_ID = 50

Последнее замечание: я заметил, что проблема в DSTINCT, моя цель - добиться одинаковой скорости (или, по крайней мере, почти одинаковой скорости) в обоих запросах.

Примечание для награды - КОНЕЦ:


Оригинальный вопрос:

Я заметил, что есть большая разница в производительности между

-- Case A
select distinct * from table where id > 1

по сравнению с (это sql, сгенерированный моим приложением Delphi)

-- Case B1
exec sp_executesql N'select distinct * from table where id > @P1',N'@P1 int',1

, что эквивалентно

-- Case B2
declare @P1 int
set @P1 = 1
select distinct * from table where id > @P1

A работает намного быстрее, чем B1 и B2. Производительность становится такой же, если я удаляю DISTINCT.

Можете ли вы прокомментировать это?

Здесь я разместил тривиальный запрос, я заметил это на запросе с 3 INNER JOIN. Во всяком случае, не сложный запрос.

Примечание: я ожидал получить ТОЧНУЮ ОДНУ ИСПОЛНИТЕЛЬСКУЮ ЭФФЕКТИВНОСТЬ в случаях A и B1 / B2.

Так есть ли некоторые предостережения в использовании DISTINCT?

UPDATE

Я попытался отключить анализ параметров с помощью DBCC TRACEON (4136, -1) (флаг, чтобы отключить анализ параметров), но ничего не изменилось. Таким образом, в этом случае проблема не связана с отсчетом параметров. Есть идеи?

Ответы [ 3 ]

2 голосов
/ 17 декабря 2010

Проблема не в том, что DISTINCT вызывает снижение производительности с параметрами, а в том, что остальная часть запроса не оптимизируется в параметризованном запросе, потому что оптимизатор не просто оптимизирует все объединения, используя 1 = @ IS_ADMINISTRATOR, как это будет с 1 = 1. Он не оптимизирует соединения без отличных, потому что он должен возвращать дубликаты на основе результата объединений.

Почему? Поскольку план выполнения, исключающий все объединения, будет недействительным для любого значения, отличного от @IS_ADMINISTRATOR = 1. Он никогда не сгенерирует этот план независимо от того, кешируете ли вы планы или нет.

Это работает так же, как и не параметризованный запрос на моем сервере 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

Что ясно из плана запроса, который я вижу на вашем примере, так это то, что @IS_ADMINISTRATOR = 1 не оптимизируется так же, как 1=1. В вашем не параметризованном примере JOINS полностью оптимизированы, и он просто возвращает каждый идентификатор в таблице DOCUMENTS (очень просто).

При @IS_ADMINISTRATOR <> 1 также отсутствуют различные оптимизации. Например, LEFT OUTER JOIN S автоматически изменяется на INNER JOIN s без предложения that OR, но они остаются как есть с that или предложением.

См. Также этот ответ: SQL LIKE% FOR INTEGERS для альтернативы динамического SQL.

Конечно, это на самом деле не объясняет разницу в производительности в вашем первоначальном вопросе, поскольку у вас нет ИЛИ. Я полагаю, это был недосмотр.

1 голос
/ 10 декабря 2010

Вы пытались запустить второй (более медленный) запрос без динамического SQL?Вы очистили кеш и перезапустили первый запрос?Возможно, вы испытываете перехват параметров с параметризованным запросом динамического SQL.

Я думаю, DISTINCT - это красная сельдь, а не актуальная проблема.

...