Melhor método para implementar uma pesquisa filtrada

17

Gostaria de perguntar sua opinião sobre a implementação de um formulário de pesquisa filtrado. Vamos imaginar o seguinte caso:

  • 1 mesa grande com muitas colunas
  • Pode ser importante dizer que este SQL Server

Você precisa implementar um formulário para pesquisar dados nesta tabela e, neste formulário, você terá várias caixas de seleção que permitem costumizar essa pesquisa.

Agora, minha pergunta aqui é qual das seguintes opções deve ser a melhor maneira de implementar a pesquisa?

  1. Crie um procedimento armazenado com uma consulta dentro. Este procedimento armazenado verificará se os parâmetros são fornecidos pelo aplicativo e, no caso em que não recebem um curinga, serão colocados na consulta.

  2. Crie uma consulta dinâmica, criada de acordo com o que é fornecido pelo aplicativo.

Estou perguntando isso porque sei que o SQL Server cria um plano de execução quando o procedimento armazenado é criado, a fim de otimizar seu desempenho, no entanto, criando uma consulta dinâmica dentro do procedimento armazenado, sacrificaremos a otimização obtida pelo plano de execução?

Por favor, diga-me qual seria a melhor abordagem em sua opinião.

j0N45
fonte
Você declara abaixo que está tendendo à solução dinâmica. Isso é legal, lembre-se de enumerar os possíveis filtros e ter índices que os suportem. Contanto que as consultas sejam construídas de forma consistente, elas devem ser eficientes.
Matthew Flynn

Respostas:

10

Convém consultar a resposta para esta pergunta semelhante aqui: /programming/11329823/add-where-clauses-to-sql-dynamically-programmatically

Descobrimos que um SPROC que utiliza vários parâmetros opcionais e implementa o filtro da seguinte maneira:

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)

armazenará em cache o primeiro plano de execução com o qual é executado (por exemplo @optionalParam1 = 'Hello World', @optionalParam2 = NULL), mas executará miseravelmente se passarmos a ele um conjunto diferente de parâmetros opcionais (por exemplo @optionalParam1 = NULL, @optionalParam2 = 42). (E, obviamente, queremos o desempenho do plano em cache, então WITH RECOMPILEestá fora)

A exceção aqui é que, se houver TAMBÉM pelo menos um filtro OBRIGATÓRIO na consulta que seja ALTAMENTE seletivo e indexado adequadamente, além dos parâmetros opcionais, o PROC acima funcionará bem.

No entanto, se TODOS os filtros forem opcionais, a terrível verdade é que o sql dinâmico parametrizado realmente funciona melhor (a menos que você escreva N! PROCS estático diferente para cada permutação de parâmetros opcionais).

SQL dinâmico como o abaixo criará e armazenará em cache um plano diferente para cada permutação dos parâmetros de consulta, mas pelo menos cada plano será 'adaptado' à consulta específica (não importa se é um PROC ou Adhoc SQL - como desde que sejam consultas parametrizadas, elas serão armazenadas em cache)

Portanto, portanto, minha preferência por:

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

etc. Não importa se passamos parâmetros redundantes para sp_executesql - eles são ignorados. Vale ressaltar que ORMs como Linq2SQL e EF usam sql dinâmico parametrizado de maneira semelhante.

StuartLC
fonte
1
Sim, eu pensei que sim, esta foi a opção que eu escolhi. Só queria ter certeza de que era uma boa. Obrigado pela resposta.
J0N45
"Se uma instrução SQL for executada sem parâmetros, o SQL Server a parametriza internamente para aumentar a possibilidade de compará-la com um plano de execução existente. Esse processo é chamado de parametrização simples". Então, basicamente, o programa pode usar algo como "where filenumber =" + filename. Claro, que abre um latas de vermes, mas isso é um tópico diferente ;-)
Codism
5

Comece com o que você acha que é mais fácil de implementar (acho que a opção 2). Em seguida, meça o desempenho dos dados do mundo real. Comece a otimizar apenas quando necessário, não antes.

A propósito, dependendo da complexidade dos seus filtros de pesquisa, sua tarefa pode não ser facilmente resolvida sem o SQL dinâmico. Portanto, mesmo quando você usa um procedimento armazenado, isso provavelmente não aumenta o desempenho, como você já suspeita. Por outro lado, se ajudar, existem vários tipos de dicas (consulte http://www.simple-talk.com/sql/performance/controlling-execution-plans-with-hints/ ) que você pode adicionar a um SQL consulta, dinâmica ou não, para ajudar o SQL Server a otimizar seu plano de execução.

Doc Brown
fonte
Bem, eu já implementei a opção 2 e acho que é o melhor caminho a seguir, principalmente porque os curingas diminuirão drasticamente o desempenho, no entanto, estou sacrificando a manutenção, porque isso aumentará a complexidade do código. Eu só queria saber se alguém conhece uma opção melhor para esse tipo de situação.
J0N45
Eu gostaria de lhe dar uma votação, mas não tenho reputação.
J0N45