El mejor método para implementar una búsqueda filtrada

17

Me gustaría preguntarle su opinión a la hora de implementar un formulario de búsqueda filtrado. Imaginemos el siguiente caso:

  • 1 mesa grande con muchas columnas
  • Puede ser importante decir que este servidor SQL

Debe implementar un formulario para buscar datos en esta tabla, y en este formulario tendrá varias casillas de verificación que le permitirán personalizar esta búsqueda.

Ahora mi pregunta aquí es ¿cuál de las siguientes opciones debería ser la mejor manera de implementar la búsqueda?

  1. Cree un procedimiento almacenado con una consulta dentro. Este procedimiento almacenado verificará si la aplicación proporciona los parámetros y, en el caso de que no se les dé un comodín, se colocará en la consulta.

  2. Cree una consulta dinámica, que se construya de acuerdo con lo que proporciona la aplicación.

Pregunto esto porque sé que SQL Server crea un plan de ejecución cuando se crea el procedimiento almacenado, para optimizar su rendimiento, sin embargo, al crear una consulta dinámica dentro del procedimiento almacenado, ¿sacrificaremos la optimización obtenida por el plan de ejecución?

Por favor, dime cuál sería el mejor enfoque en tu opinión.

j0N45
fuente
A continuación declara que tiende a la solución dinámica. Eso es genial, solo asegúrate de enumerar los posibles filtros y tener índices que los admitan. Mientras las consultas se construyan de manera consistente, deberían ser eficientes.
Matthew Flynn

Respuestas:

10

Es posible que desee ver la respuesta a esta pregunta similar aquí: /programming/11329823/add-where-clauses-to-sql-dynamically-programmatic

Hemos encontrado que un SPROC que toma un montón de parámetros opcionales e implementa el filtro de esta manera:

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)

almacenará en caché el primer plan de ejecución con el que se ejecuta (p @optionalParam1 = 'Hello World', @optionalParam2 = NULL. ej. ) pero luego funcionará miserablemente si le pasamos un conjunto diferente de parámetros opcionales (p @optionalParam1 = NULL, @optionalParam2 = 42. ej .). (Y, obviamente, queremos el rendimiento del plan en caché, por lo que WITH RECOMPILEestá fuera)

La excepción aquí es que si TAMBIÉN hay al menos un filtro OBLIGATORIO en la consulta que es ALTAMENTE selectivo e indexado correctamente, además de los parámetros opcionales, entonces el PROC anterior funcionará bien.

Sin embargo, si TODOS los filtros son opcionales, la verdad bastante horrible es que sql dinámico parametrizado realmente funciona mejor (a menos que escriba N! PROCS estáticos diferentes para cada permutación de parámetros opcionales).

El SQL dinámico como el siguiente creará y almacenará en caché un plan diferente para cada permutación de los parámetros de Consulta, pero al menos cada plan se 'adaptará' a la consulta específica (no importa si es un PROC o Adhoc SQL, como siempre que sean consultas parametrizadas, se almacenarán en caché)

De ahí mi preferencia 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. No importa si pasamos parámetros redundantes a sp_executesql, se ignoran. Vale la pena señalar que los ORM como Linq2SQL y EF usan sql dinámico parametrizado de una manera similar.

StuartLC
fuente
1
Sí, eso pensé, esta fue la opción que elegí. Solo quería asegurarme de que fuera buena. Gracias por la respuesta.
j0N45
"Si una declaración SQL se ejecuta sin parámetros, SQL Server parametriza la declaración internamente para aumentar la posibilidad de compararla con un plan de ejecución existente. Este proceso se llama parametrización simple". Básicamente, el programa puede usar algo como "where filenumber =" + filename. Por supuesto, eso abre una lata de gusanos, pero ese es un tema diferente ;-)
Codism
5

Comienza con lo que creas que es más fácil de implementar (supongo que la opción 2). Luego mida el rendimiento de los datos del mundo real. Solo comience a optimizar cuando sea necesario, no de antemano.

Por cierto, dependiendo de cuán complejos sean sus filtros de búsqueda, su tarea puede no resolverse fácilmente sin SQL dinámico. Entonces, incluso cuando utiliza un procedimiento almacenado, lo más probable es que no aumente el rendimiento, como ya sospecha. Por otro lado, si ayuda, hay varios tipos de sugerencias (consulte http://www.simple-talk.com/sql/performance/controlling-execution-plans-with-hints/ ) que puede agregar a un SQL consulta, dinámica o no, para ayudar al servidor SQL a optimizar su plan de ejecución.

Doc Brown
fuente
Bueno, ya implementé la opción 2 y creo que es la mejor manera de hacerlo, principalmente porque los comodines disminuirán drásticamente el rendimiento, sin embargo, estoy sacrificando el mantenimiento, ya que esto aumentará la complejidad del código. Solo quería saber si alguien conoce una mejor opción para este tipo de situaciones.
j0N45
Le daría un voto positivo, pero no tengo reputación, lo siento.
j0N45