web-dev-qa-db-fra.com

Meilleure méthode pour implémenter une recherche filtrée

Je voudrais vous demander votre avis lorsqu'il s'agit de mettre en œuvre un formulaire de recherche filtré. Imaginons l'affaire suivante:

  • 1 grande table avec beaucoup de colonnes
  • Il est peut-être important de dire que ce serveur SQL

Vous devez implémenter un formulaire pour rechercher des données dans ce tableau et dans ce formulaire, vous aurez plusieurs cases à cocher vous permettant de consulter cette recherche.

Maintenant, ma question ici est la suivante: laquelle des éléments suivants devrait être le meilleur moyen de mettre en œuvre la recherche?

  1. Créez une procédure stockée avec une requête à l'intérieur. Cette procédure stockée vérifiera si les paramètres sont donnés par l'application et, dans le cas où ils ne sont pas donnés, une carte générique sera placée dans la requête.

  2. Créez une requête dynamique, qui est construite en conséquence de ce qui est donné par l'application.

Je demande cela parce que je sais que SQL Server crée un plan d'exécution lorsque la procédure stockée est créée, afin d'optimiser ses performances, cependant, en créant une requête dynamique à l'intérieur de la procédure stockée, permettra de sacrifierons l'optimisation gagnée par le plan d'exécution?

S'il vous plaît dites-moi quelle serait la meilleure approche de votre adpinion.

17
j0N45

Vous voudrez peut-être regarder la réponse à cette question similaire ici: https://stackoverflow.com/questions/11329823/add-where-claus-a-sql-dynamiquement-pogmatiquement

Nous avons constaté qu'une SPUCT qui prend un tas de paramètres facultatifs et implémente le filtre comme celui-ci:

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)

cache le premier plan d'exécution qu'il est exécuté (par ex. @optionalParam1 = 'Hello World', @optionalParam2 = NULL), puis effectuez misérablement si nous transmettons un ensemble différent de paramètres facultatifs (par exemple @optionalParam1 = NULL, @optionalParam2 = 42). (Et évidemment, nous voulons la performance du plan mis en cache, alors WITH RECOMPILE est sorti)

L'exception est que, s'il existe également au moins un filtre obligatoire sur la requête hautement sélective et correctement indexé, en plus des paramètres facultatifs, le PROP ci-dessus va fonctionner correctement.

Toutefois, si tous les filtres sont facultatifs, la vérité plutôt terrible est que le SQL dynamique paramétré fonctionne réellement mieux (à moins que vous n'écris N! Procs statiques différents pour chaque permutation de paramètres facultatifs).

SQL dynamique comme ci-dessous créera et cache un plan différent pour chaque permutation des paramètres de requête, mais au moins chaque plan sera "adapté" à la requête spécifique (peu importe s'il s'agit d'un procédé ou d'un SQL adhoc - comme Tant qu'ils sont des requêtes paramétrées, ils seront mis en cache)

Alors, donc ma préférence pour:

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. Peu importe que nous passions dans des paramètres redondants dans sp_executesql - ils sont ignorés. Il convient de noter que l'ORM est comme LINQ2SQL et EF utilise SQL dynamique paramétrée de manière similaire.

L'horrible 1 == 1 Le hack peut également être évité si vous gardez une trace de si les prédicats ont encore été appliqués ou non, puis appliquent de manière conditionnelle le premier AND uniquement sur la seconde et les prédicats suivants. S'il n'y a pas de prédicats du tout, alors WHERE disparaît également.

Notez que malgré la requête dynamique, nous sommes toujours parameterizing les filtres, le cas échéant, nous avons donc au moins une première ligne de défense contre les attaques d'injection SQL.

11
StuartLC