Влияние производительности условий SQL 'ИЛИ', когда одна альтернатива тривиальна? - PullRequest
3 голосов
/ 24 февраля 2009

Я создаю хранимую процедуру для поиска некоторых данных в моей базе данных в соответствии с некоторыми критериями, введенными пользователем.

Мой SQL-код выглядит так:

Create Procedure mySearchProc
(
@IDCriteria bigint=null,
...
@MaxDateCriteria datetime=null
)
as
select Col1,...,Coln from MyTable 
where (@IDCriteria is null or ID=@IDCriteria)
...
and (@MaxDateCriteria is null or Date<@MaxDateCriteria)

Редактировать : у меня около 20 возможных параметров, и каждая комбинация из n ненулевых параметров может иметь место.

Можно ли написать код такого типа с точки зрения производительности? (Я использую MS SQL Server 2008)

Будет ли генерация кода SQL, содержащего только необходимые выражения where, значительно быстрее?

Ответы [ 12 ]

3 голосов
/ 24 февраля 2009

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

2 голосов
/ 24 февраля 2009
where (@IDCriteria is null or ID=@IDCriteria)
  and (@MaxDateCriteria is null or Date<@MaxDateCriteria)

Если вы напишите этот критерий, то SQL-сервер не будет знать, лучше ли использовать индекс для идентификаторов или индекс для дат.

Для правильной оптимизации гораздо лучше написать отдельные запросы для каждого случая и использовать IF, чтобы привести вас к правильному.

IF @IDCriteria is not null and @MaxDateCriteria is not null

  --query
  WHERE ID = @IDCriteria and Date < @MaxDateCriteria

ELSE IF @IDCriteria is not null

  --query
  WHERE ID = @IDCriteria

ELSE IF @MaxDateCriteria is not null

  --query
  WHERE Date < @MaxDateCriteria

ELSE

  --query
  WHERE 1 = 1

Если вы ожидаете, что из оптимизатора понадобятся другие планы, вам нужно написать разные запросы, чтобы получить их !!

Будет ли генерация кода SQL, содержащего только необходимые выражения where, значительно быстрее?

Да - если вы ожидаете, что оптимизатор будет выбирать между разными планами.


Edit:

DECLARE @CustomerNumber int, @CustomerName varchar(30)

SET @CustomerNumber = 123
SET @CustomerName = '123'

SELECT * FROM Customers
WHERE (CustomerNumber = @CustomerNumber OR @CustomerNumber is null)
  AND (CustomerName = @CustomerName OR @CustomerName is null)

CustomerName и CustomerNumber индексируются. Оптимизатор говорит: «Кластерный Сканирование индекса с распараллеливанием ". Вы не можете написать худший запрос к одной таблице.


Редактировать: у меня есть около 20 возможных параметров, и каждая комбинация из n ненулевых параметров может иметь место.

У нас была похожая функция поиска в нашей базе данных. Когда мы посмотрели на фактические запросы, 99,9% из них использовали AccountIdentifier. В вашем случае я подозреваю, что либо один столбец - всегда предоставлен - либо один из двух столбцов всегда указан. Это приведет к 2 или 3 случаям соответственно.

Не важно удалять ИЛИ из всей структуры. Важно удалить OR из столбцов / столбцов, которые, как вы ожидаете, будет использовать оптимизатор для доступа к индексам.

1 голос
/ 25 февраля 2009

Итак, чтобы свести выше комментарии:

Создайте отдельную подпроцедуру для каждой из самых популярных вариаций конкретных комбинаций параметров, и в пределах процедуры диспетчера вызовите соответствующую подпрограмму из структуры IF ELSE, предпоследнее предложение ELSE которой динамически создает запрос для покрытия остальные случаи.

Возможно, сначала могут быть специально закодированы только один или два случая, но со временем и определенные комбинации параметров будут определены как статистически значимые, могут быть написаны процедуры реализации и расширена основная конструкция IF ELSE для идентификации этих случаев и вызовите соответствующую подпроцедуру.

0 голосов
/ 26 февраля 2009

Каждый раз, когда процедура вызывается, передавая разные параметры, существует разный оптимальный план выполнения для получения данных. Проблема в том, что SQL кэшировал план выполнения для вашей процедуры и будет использовать неоптимальный (читай ужасный) план выполнения.

Я бы порекомендовал:

  1. Создание определенных SP для часто выполняемых путей выполнения (т. Е. Переданных наборов параметров), оптимизированных для каждого сценария.
  2. Сохраняйте основной универсальный SP для крайних случаев (предполагая, что они выполняются редко), но используйте предложение WITH RECOMPILE, чтобы при каждом запуске процедуры создавался новый план выполнения.

Мы используем операторы OR, проверяющие NULL на необязательные параметры, оказывающие большое влияние. Он работает очень хорошо без опции RECOMPILE, пока путь выполнения не кардинально изменяется при передаче различных параметров.

0 голосов
/ 25 февраля 2009

Мое предложение - создать строку sql. Вы получите максимальную производительность от индексации и повторно используете план выполнения.

DECLARE @sql nvarchar(4000);
SET @sql = N''

IF @param1 IS NOT NULL
    SET @sql = CASE WHEN @sql = N'' THEN N'' ELSE N' AND ' END + N'param1 = @param1';
IF @param2 IS NOT NULL
    SET @sql = CASE WHEN @sql = N'' THEN N'' ELSE N' AND ' END + N'param2 = @param2';
...
IF @paramN IS NOT NULL
    SET @sql = CASE WHEN @sql = N'' THEN N'' ELSE N' AND ' END + N'paramN = @paramN';

IF @sql <> N''
    SET @sql = N' WHERE ' + @sql;
SET @sql = N'SELECT ... FROM myTable' + @sql;

EXEC sp_executesql @sql, N'@param1 type, @param2 type, ..., @paramN type', @param1, @param2, ..., @paramN;
0 голосов
/ 25 февраля 2009

В настоящее время я работаю с SQL 2005, поэтому не знаю, работает ли оптимизатор 2008 иначе. При этом я обнаружил, что вам нужно сделать пару вещей ...

  1. Убедитесь, что вы используете WITH (RECOMPILE) для вашего запроса

  2. Используйте операторы CASE, чтобы вызвать короткое замыкание логики. По крайней мере, в 2005 году это НЕ делается с помощью операторов OR. Например:

.

SELECT
     ...
FROM
     ...
WHERE
     (1 =
          CASE
               WHEN @my_column IS NULL THEN 1
               WHEN my_column = @my_column THEN 1
               ELSE 0
          END
     )

Оператор CASE заставит оптимизатор SQL Server распознать, что ему не нужно продолжать после первого WHEN. В этом примере это не имеет большого значения, но в моих поисковых программах непустой параметр часто означал поиск в другой таблице через подзапрос на наличие соответствующей строки, что стоило дорого. Как только я сделал это изменение, поисковые процессы начали работать намного быстрее.

0 голосов
/ 25 февраля 2009

Я предлагаю на шаг дальше, чем некоторые другие предложения - подумать о вырождении на гораздо более высоком уровне абстракции, предпочтительно структуре пользовательского интерфейса. Обычно это происходит, когда обдумывается проблема в режиме данных, а не в режиме пользовательского домена.

На практике я обнаружил, что почти каждый такой запрос имеет один или несколько ненулевых, довольно избирательных столбцов, которые были бы разумно оптимизированы, если бы был указан один (или более). Кроме того, обычно это разумные предположения, которые могут понять пользователи.

Пример: Найти заказы по клиенту; или Найти заказы по диапазону дат; или Найти заказы по продавцу.

Если этот шаблон применяется, то вы можете разложить ваш гипергенерализованный запрос на более целенаправленные подзапросы, которые также имеют смысл для пользователей, и вы можете разумно запрашивать требуемые значения (или диапазоны) и не беспокоиться о создании эффективных выражений для дочерней столбцы.

Вы все равно можете оказаться в категории "Все другие". Но, по крайней мере, если вы предоставите открытую форму Query By Example, пользователи будут иметь некоторое представление о том, во что они ввязываются. Выполнение того, что вы описываете, на самом деле ставит вас перед попыткой обдумать оптимизатор запросов, что, на мой взгляд, глупо.

0 голосов
/ 25 февраля 2009

это один из случаев, когда я использую сборку кода или sproc для каждого поиска. так как ваш поиск настолько сложен, я бы пошел с построением кода. Вы можете сделать это либо в коде, либо с помощью динамического SQL. просто будьте осторожны с SQL-инъекцией.

0 голосов
/ 25 февраля 2009

Выбор правильного индекса труден для оптимизатора. ИМО, это один из немногих случаев, когда динамический SQL - лучший вариант.

0 голосов
/ 25 февраля 2009

Что если такие конструкции были заменены:

WHERE (@IDCriteria IS NULL OR @IDCriteria=ID)
AND (@MaxDateCriteria IS NULL OR Date<@MaxDateCriteria)
AND ...

с такими, как эти:

WHERE ID = ISNULL(@IDCriteria, ID)
AND Date < ISNULL(@MaxDateCriteria, DATEADD(millisecond, 1, Date))
AND ...

или это просто тот же неоптимизируемый запрос в синтаксическом сахаре?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...