T-SQL.Каков наилучший подход для реализации «любого», где заявление - PullRequest
2 голосов
/ 14 марта 2019

Представьте, что у вас есть таблица заказов со статусами:

Id (PK)
Status (INT) 
... 

Существует пользовательский интерфейс (UI), где бизнес-пользователь может выбирать, какой статус для заказов ему интересен. Есть магическое число "-1 "это означает - пользователь интересуется каждым статусом

Каков наилучший способ и соображения по реализации запроса:

1. select * from orders where status = @user-selected-status-id or @user-selected-status-id = -1

2. select * from orders where status = ISNULL(NULLIF(@user-selected-status-id, -1), status) 

3. Dynamic sql approach with string concatenation "where" operator is joined only if required (EXEC sp_executesql at the very end) 

Я использовал« План выполнения »для всех трех подходов иэто говорит о том, что оценочная стоимость является самой лучшей для динамического sql, тогда isnull / nullif и "or" - посторонний.

Раньше я считал, что подход «или» должен быть лучше, потому что это подход краткости / недействительности

Что меня еще больше позабавило - что результаты (оценки) зависят от размера таблицы заказов.Когда он вырос в десять раз (из 10K -> 100K строк), аутсайдер был "динамическим sql".

Могу ли я доверять этому?

1 Ответ

3 голосов
/ 14 марта 2019

Гейл Шоу написал несколько замечательных статей на эту тему: Catch-all Queries и Пересмотр Catch-all Queries .

Сводка этого, однако, такова, что вы находитесь в 2008 году, а затем - параметризованные динамические операторы. Если вы работаете на SQL Server 2012+, тогда введите WHERE Column = @Variable OR @Variable IS NULL и добавьте OPTION (RECOMPILE) в конец.

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

Что бы ни случилось, не идите с status = ISNULL(NULLIF(@user-selected-status-id, -1), status). Это предложение не является SARGable и будет иметь худшую производительность из всех.

Итак, метод 2012+ (для плана запроса не высокой сложности с точки зрения генерации) будет:

SELECT {Columns}
FROM YourTable YT
WHERE (YT.Column = @Variable
   OR  @Variable IS NULL)
OPTION (RECOMPILE);

OPTION (RECOMPILE) важен, так как он заставляет механизм данных заново создавать план запроса (и оценки) при каждом запуске запроса. Если у вас есть запрос, где @Variable имеет значение, то число строк, которые будут возвращены, будет оцениваться как «низкое» (соответственно). С другой стороны, для запроса, где значение @Variable равно NULL, это означает, что каждая строка будет возвращена (в этом простом запросе); или «большое» количество строк. Планы кэширования в SQL Server. Это означает, что план с небольшим числом не подходит для большого числа. Плохие оценки могут (и могут) означать низкую производительность, поскольку в базе данных tempdb могут возникать проблемы с переполнением оперативной памяти, а время ресурсов не соответствует ожидаемому. Принуждение механизма данных к воссозданию плана означает, что эти оценки будут воссозданы для каждого прогона (что обойдется дорого), но они будут намного лучше для выполняемого запроса; при условии, что ваша статистика тоже актуальна.

Если, однако, вы используете SQL Server 2008, у вас нет доступа к OPTION (RECOMPILE), поэтому вам нужно идти по динамическому маршруту:

DECLARE @SQL nvarchar(MAX);

SET @SQL = N'SELECT {Columns}' + NCHAR(13) + NCHAR(10) +
           N'FROM YourTable' + NCHAR(13) + NCHAR(10) +
           CASE WHEN @Variable IS NOT NULL THEN N'WHERE YT.Column = @Variable' ELSE '' END + N';';

EXEC sp_executesql @SQL, N'@Variable {DataType}', @Variable = @Variable;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...