Каковы некоторые рекомендации по оптимизации SQL-запросов LIKE с несколькими столбцами? - PullRequest
2 голосов
/ 23 января 2009

У меня есть поисковый запрос, который я наследую и пытаюсь оптимизировать. Мне любопытно услышать, есть ли у кого-нибудь лучшие практики и рекомендации для этого. Рабочий сервер все еще работает на SQL Server 2000.

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

INSERT INTO   @tempCustTable (CustomerID, FirstName, LastName, City, StateProvince, Zip, PhoneNumber)
SELECT  DISTINCT cu.CustomerID, cu.FirstName, cu.LastName, a.City,
a.StateProvince, a.Zip, p.PhoneNumber
FROM Customer cu WITH(NOLOCK)
LEFT OUTER JOIN Address a WITH(NOLOCK) ON cu.CustomerID = a.CustomerID
LEFT OUTER JOIN Phone p WITH(NOLOCK) ON cu.CustomerID = p.CustomerID
WHERE  (cu.LastName = @LastName OR cu.LastName LIKE @LastName + '%') 
AND (@FirstName IS NULL OR cu.FirstName = @FirstName OR cu.FirstName LIKE @FirstName + '%')
AND (@StateProvince = '' OR a.StateProvince LIKE @StateProvince)
AND (@City = '' OR a.City LIKE @City + '%')
AND (@Zip = '' OR a.Zip = @Zip OR a.Zip LIKE @Zip + '%')
ORDER BY cu.LastName, cu.FirstName

У кого-нибудь есть рекомендации по улучшению производительности запроса?

Ответы [ 5 ]

2 голосов
/ 23 января 2009

не вся эта строка

AND (@Zip = '' OR a.Zip = @Zip OR a.Zip LIKE @Zip + '%')

так же, как этот

AND (a.Zip LIKE @Zip + '%')

точно

AND (a.Zip LIKE @Zip + '%')

это то же самое, что

a.Zip = @Zip OR a.Zip LIKE @Zip + '%'
1 голос
/ 23 января 2009

Вы можете определенно устранить большую часть избыточности в своем коде, как SQLMenace указал в качестве начала.

Другое дело, ORDER BY не должен использоваться с INSERT..SELECT. ORDER BY не имеет смысла в этом контексте. Люди иногда используют его, чтобы заставить колонку IDENTITY вести себя определенным образом, но это плохая привычка IMO.

Не знаю, поможет ли это в вашей ситуации, но одна вещь, с которой я недавно столкнулся, заключалась в том, что в хранимых процедурах SQL Server (я использую 2005, но, вероятно, также и для 2000) не будет коротким. Схема ИЛИ условие во многих случаях. Например, когда вы используете:

@my_parameter IS NULL OR my_column = @my_parameter

он все равно будет оценивать вторую половину, даже если вы передадите значение NULL для @my_parameter. Это произошло даже тогда, когда я установил хранимую процедуру для перекомпиляции (и SELECT). Хитрость заключалась в том, чтобы вызвать короткое замыкание с помощью оператора CASE. Используя этот трюк (и удалив некоторую избыточность), ваш оператор будет выглядеть так:

INSERT INTO @tempCustTable
(
     CustomerID,
     FirstName,
     LastName,
     City,
     StateProvince,
     Zip,
     PhoneNumber
)
SELECT DISTINCT
     cu.CustomerID,
     cu.FirstName,
     cu.LastName,
     a.City,
     a.StateProvince,
     a.Zip,
     p.PhoneNumber
FROM Customer cu WITH(NOLOCK)
LEFT OUTER JOIN Address a WITH(NOLOCK) ON cu.CustomerID = a.CustomerID
LEFT OUTER JOIN Phone p WITH(NOLOCK) ON cu.CustomerID = p.CustomerID
WHERE
     (cu.LastName LIKE @LastName + '%') AND
     (1 =
          CASE
               WHEN @FirstName IS NULL THEN 1
               WHEN cu.FirstName LIKE @FirstName + '%' THEN 1
               ELSE 0
          END
     ) AND
     (1 =
          CASE
               WHEN @StateProvince = '' THEN 1
               WHEN a.StateProvince = @StateProvince THEN 1
               ELSE 0
          END
     ) AND
     (1 = CASE
               WHEN @City = '' THEN 1
               WHEN a.City LIKE @City + '%' THEN 1
               ELSE 0
          END
     ) AND
     (1 = CASE
               WHEN @Zip = '' THEN 1
               WHEN a.Zip LIKE @Zip + '%' THEN 1
               ELSE 0
          END
     )

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

Наконец ... будьте совместимы с вашими параметрами. Для @FirstName вы проверяете значение NULL, чтобы определить, используется оно или нет, но для других вы проверяете наличие пустых строк. Базовое кодирование 101, с которым вам нужно быть осторожным.

0 голосов
/ 23 января 2009

Вы можете создать запрос с помощью динамического sql. Это избавит вас от большинства ваших OR и будет также означать, что вам нужно будет только включить в строки оператора WHERE параметры, которые пользователь действительно вводит.

Если вы сделаете это, обязательно используйте sp_executesql, а не exec, чтобы вы могли параметризовать динамический sql, чтобы можно было кэшировать план запроса.

0 голосов
/ 23 января 2009

Я бы постарался, чтобы в моем коде sql не было добавлено «%», а вместо этого ожидал, что параметр уже имеет его, это, конечно, после того, как вы проверили его в своем приложении! Тогда не включайте сравнения =, используйте LIKE все время:

ГДЕ (cu.LastName LIKE @LastName)

вместо:

ГДЕ (cu.LastName = @LastName ИЛИ cu.LastName LIKE @LastName + '%')

0 голосов
/ 23 января 2009
  • Избегайте "ИЛИ" - они вообще предотвращают использование индексов
  • Никогда не ставьте «%» на левой стороне. - та же причина.
...