Лучший метод для реализации фильтрованного поиска

17

Я хотел бы спросить ваше мнение, когда дело доходит до реализации фильтрованной формы поиска. Давайте представим следующий случай:

  • 1 большой стол с множеством колонн
  • Может быть важно сказать, что этот SQL Server

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

Теперь у меня вопрос: какой из следующих способов должен быть наилучшим для осуществления поиска?

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

  2. Создайте динамический запрос, построенный в соответствии с тем, что дано приложением.

Я спрашиваю об этом, потому что знаю, что SQL Server создает план выполнения при создании хранимой процедуры, чтобы оптимизировать ее производительность, однако, создав динамический запрос внутри хранимой процедуры, пожертвуем ли мы оптимизацией, полученной планом выполнения?

Пожалуйста, скажите мне, что было бы лучшим подходом в вашем мнении.

j0N45
источник
Ниже вы заявляете, что стремитесь к динамическому решению. Это круто, просто убедитесь, что вы перечислили возможные фильтры и имеете индексы, поддерживающие их. Пока запросы строятся последовательно, они должны быть эффективными.
Мэтью Флинн

Ответы:

10

Возможно, вы захотите посмотреть ответ на этот похожий вопрос здесь: /programming/11329823/add-where-clauses-to-sql-dynamically-programmatics

Мы обнаружили, что SPROC принимает множество необязательных параметров и реализует фильтр следующим образом:

CREATE PROC MyProc (@optionalParam1 NVARCHAR(50)=NULL, @optionalParam2 INT=NULL)
AS 
...
SELECT field1, field2, ... FROM [Table]
WHERE 
  (@optionalParam1 IS NULL OR MyColumn1 = @optionalParam1)
  AND (@optionalParam2 IS NULL OR MyColumn2 = @optionalParam2)

будет кешировать первый план выполнения, с которым он запускается (например @optionalParam1 = 'Hello World', @optionalParam2 = NULL), но затем выполнится с треском, если мы передадим ему другой набор необязательных параметров (например @optionalParam1 = NULL, @optionalParam2 = 42). (И, очевидно, мы хотим, чтобы производительность кэшированного плана, так WITH RECOMPILEчто нет)

Исключением здесь является то, что если в запросе также есть хотя бы один ОБЯЗАТЕЛЬНЫЙ фильтр, который ВЫСОКО селективен и правильно проиндексирован, в дополнение к необязательным параметрам, то вышеупомянутый PROC будет работать нормально.

Однако, если ВСЕ фильтры являются необязательными, довольно ужасная истина заключается в том, что параметризованный динамический sql действительно работает лучше (если вы не напишите N! Различных статических PROCS для каждой перестановки необязательных параметров).

Динамический SQL, подобный приведенному ниже, будет создавать и кэшировать отдельный план для каждой перестановки параметров запроса, но по крайней мере каждый план будет «адаптирован» для конкретного запроса (не имеет значения, является ли он PROC или Adhoc SQL - как Пока они являются параметризованными запросами, они будут кэшироваться)

Поэтому отсюда мое предпочтение:

DECLARE @SQL NVARCHAR(MAX)        

-- Mandatory / Static part of the Query here
SET @SQL = N'SELECT * FROM [table] WHERE 1 = 1'

IF @OptionalParam1 IS NOT NULL        
    BEGIN        
        SET @SQL = @SQL + N' AND MyColumn1 = @optionalParam1'    
    END        

IF @OptionalParam2 IS NOT NULL        
    BEGIN        
        SET @SQL = @SQL + N' AND MyColumn2 = @optionalParam2'    
    END        

EXEC sp_executesql @SQL,        
    N'@optionalParam1 NVARCHAR(50), 
      @optionalParam2 INT'
    ,@optionalParam1 = @optionalParam1
    ,@optionalParam2 = @optionalParam2

и т.д. Неважно, если мы передадим избыточные параметры в sp_executesql - они игнорируются. Стоит отметить, что ORM, такие как Linq2SQL и EF, используют параметризованный динамический sql аналогичным образом.

StuartLC
источник
1
Да, я так и думал, это был вариант, который я выбрал. Просто хотел убедиться, что это было хорошо. Спасибо за ответ.
j0N45
«Если оператор SQL выполняется без параметров, SQL Server параметризует этот оператор внутри, чтобы увеличить возможность сопоставления его с существующим планом выполнения. Этот процесс называется простой параметризацией». Так что в основном программа может использовать что-то вроде «где filenumber =» + имя файла. Конечно, это открывает банки с червями, но это уже другая тема ;-)
Codism
5

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

Кстати, в зависимости от того, насколько сложны ваши поисковые фильтры, ваша задача не может быть легко решена без динамического SQL. Таким образом, даже если вы используете хранимую процедуру, это, скорее всего, не увеличит производительность, как вы уже подозревали. С другой стороны, если это помогает, есть несколько типов подсказок (см. Http://www.simple-talk.com/sql/performance/controlling-execution-plans-with-hints/ ), которые можно добавить в SQL запрос, динамический или нет, чтобы помочь SQL-серверу оптимизировать свой план выполнения.

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