Производительность SQL Server параметризованных запросов с ведущими подстановочными знаками - PullRequest
6 голосов
/ 16 августа 2010

У меня есть база данных SQL 2008 R2 с около 2 миллионами строк в одной из таблиц, и я испытываю трудности с выполнением определенного запроса при использовании параметризованного SQL.

В таблице есть поле с именем:

[PatientsName] nvarchar NULL,

На поле также есть простой индекс:


CREATE NONCLUSTERED INDEX [IX_Study_PatientsName] ON [dbo].[Study] 
(
    [PatientsName] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, FILLFACTOR = 90) ON [INDEXES]
GO

Когда я выполняю этот запрос в студии управления, выполнение занимает около 4 секунд:


declare @StudyPatientsName nvarchar(64)
set @StudyPatientsName= '%Jones%'

SELECT COUNT(*) FROM Study WHERE Study.PatientsName like @StudyPatientsName

Но когда я выполняю этот запрос:


SELECT COUNT(*) FROM Study WHERE Study.PatientsName like '%Jones%'

это занимает чуть больше полсекунды, чтобы выполнить.

Глядя на планы выполнения, запрос без параметризации выполняет сканирование индекса с использованием вышеупомянутого индекса, что, очевидно, является эффективным. Параметризованный запрос использует индекс, но выполняет поиск диапазона по индексу.

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

У нас есть собственный ORM, который выполняет параметризованные запросы, в которых возникла проблема. Эти запросы выполняются на основе ввода от пользователя, поэтому параметризованные запросы имеют смысл избегать таких вещей, как атака с использованием SQL-инъекций. Мне интересно, есть ли способ сделать функцию параметризованного запроса так же, как и не параметризованный запрос?

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


SELECT COUNT(*) FROM Study WHERE Study.PatientsName like @StudyPatientsName
OPTION ( OPTIMIZE FOR (@StudyPatientsName = '%Jones%'))

, который был упомянут как решение в этом вопросе, но это не имело значения.

Буду признателен за любую помощь.

Ответы [ 4 ]

4 голосов
/ 16 августа 2010

Похоже, вы хотите принудительно сканировать.Есть подсказка FORCESEEK, но я не вижу аналогичной подсказки FORCESCAN.Это должно сделать это, хотя.

SELECT COUNT(*) 
FROM Study 
WHERE Study.PatientsName + '' like @StudyPatientsName

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

SELECT COUNT(*) 
FROM Study 
WHERE Study.PatientsName  like @StudyPatientsName
option (recompile)
3 голосов
/ 16 августа 2010

Я думаю, что ваш лучший шанс улучшить производительность здесь - изучить использование полнотекстового индекса .

0 голосов
/ 17 августа 2010

Если ничего не помогло, вы можете попробовать

SELECT COUNT(*) FROM Study WITH(INDEX(0)) WHERE Study.PatientsName like @StudyPatientsName

Возможно, вы могли бы обернуть его в IF

IF substring(@StudyPatientsName, 1, 1) = '%'
    SELECT COUNT(*) FROM Study WITH(INDEX(0)) WHERE Study.PatientsName like @StudyPatientsName
ELSE
    SELECT COUNT(*) FROM Study WHERE Study.PatientsName like @StudyPatientsName

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

0 голосов
/ 16 августа 2010

У меня проблемы с поиском документации, чтобы проверить это, но IIRC, COUNT (*) выполняет полное сканирование таблицы в MS SQL (в отличие от использования кэшированного значения). Если вы запустите его для столбца, который не может быть нулевым и / или имеет определенный индекс, я верю (опять же, все еще не могу найти документы для подтверждения, поэтому я могу быть неосновным здесь), который будет быстрее.

Что происходит, когда вы изменяете запрос на что-то вроде:

SELECT COUNT(id) FROM Study WHERE Study.PatientsName Like @StudyPatientsName

или

SELECT COUNT(PatientsName) FROM Study 
WHERE Study.PatientsName 
LIKE @StudyPatientsName
...