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

17

Je voudrais vous demander votre avis quant à l'implémentation d'un formulaire de recherche filtré. Imaginons le cas suivant:

  • 1 grande table avec beaucoup de colonnes
  • Il pourrait ê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 qui vous permettront de personnaliser cette recherche.

Maintenant, ma question ici est laquelle parmi les suivantes devrait être la meilleure façon 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, un caractère générique sera mis dans la requête.

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

Je pose cette question 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, mais en créant une requête dynamique à l'intérieur de la procédure stockée, sacrifierons-nous l'optimisation obtenue par le plan d'exécution?

Veuillez me dire quelle serait la meilleure approche à votre avis.

j0N45
la source
Vous déclarez ci-dessous que vous tendez vers la solution dynamique. C'est cool, assurez-vous simplement d'énumérer les filtres possibles et d'avoir des index les prenant en charge. Tant que les requêtes sont construites de manière cohérente, elles doivent être efficaces.
Matthew Flynn du

Réponses:

10

Vous voudrez peut-être consulter la réponse à cette question similaire ici: /programming/11329823/add-where-clauses-to-sql-dynamically-programmatically

Nous avons constaté qu'un SPROC qui prend un tas de paramètres facultatifs et implémente le filtre comme ceci:

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)

mettra en cache le premier plan d'exécution avec lequel il est exécuté (par exemple @optionalParam1 = 'Hello World', @optionalParam2 = NULL), mais fonctionnera misérablement si nous lui transmettons un ensemble différent de paramètres facultatifs (par exemple @optionalParam1 = NULL, @optionalParam2 = 42). (Et évidemment, nous voulons les performances du plan mis en cache, il en WITH RECOMPILEva de même)

L'exception ici est que s'il y a AUSSI au moins un filtre OBLIGATOIRE sur la requête qui est HAUTEMENT sélectif et correctement indexé, en plus des paramètres facultatifs, alors le PROC ci-dessus fonctionnera correctement.

Cependant, si TOUS les filtres sont facultatifs, la vérité plutôt horrible est que SQL dynamique paramétré fonctionne réellement mieux (à moins que vous n'écriviez N! PROCS statiques différents pour chaque permutation de paramètres facultatifs).

Le SQL dynamique comme ci-dessous créera et mettra en 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 qu'il s'agisse d'un SQL PROC ou Adhoc - comme tant qu'il s'agit de requêtes paramétrées, elles seront mises en cache)

D'où 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 si nous passons des paramètres redondants dans sp_executesql - ils sont ignorés. Il convient de noter que les ORM tels que Linq2SQL et EF utilisent SQL dynamique paramétré de manière similaire.

StuartLC
la source
1
Oui, je le pensais, c'est l'option que j'ai choisie. Je voulais juste m'assurer qu'elle était bonne. Merci pour votre réponse.
j0N45
"Si une instruction SQL est exécutée sans paramètres, SQL Server paramètre cette instruction en interne pour augmenter la possibilité de la faire correspondre avec un plan d'exécution existant. Ce processus est appelé paramétrage simple." Donc, fondamentalement, le programme peut utiliser quelque chose comme "où filenumber =" + filename. Bien sûr, cela ouvre une boîte de vers mais c'est un sujet différent ;-)
Codism
5

Commencez par ce que vous pensez être plus facile à mettre en œuvre (je suppose que l'option 2). Mesurez ensuite les performances des données du monde réel. Ne commencez l'optimisation qu'en cas de besoin, pas avant.

Soit dit en passant, selon la complexité de vos filtres de recherche, votre tâche peut ne pas être facilement résolue sans SQL dynamique. Ainsi, même lorsque vous utilisez une procédure stockée, cela n'augmentera probablement pas les performances, comme vous le soupçonnez déjà. D'un autre côté, si cela aide, il existe plusieurs types d'indices (voir http://www.simple-talk.com/sql/performance/controlling-execution-plans-with-hints/ ) que vous pouvez ajouter à un SQL requête, dynamique ou non, pour aider SQL Server à optimiser son plan d'exécution.

Doc Brown
la source
Eh bien, j'ai déjà implémenté l'option 2 et je pense que c'est la meilleure façon de procéder, principalement parce que les caractères génériques diminueront considérablement les performances, mais je sacrifie la maintenance, car cela augmentera la complexité du code. Je voulais juste savoir si quelqu'un connaissait une meilleure option pour ce genre de situations.
j0N45
Je vous donnerais un vote positif, mais je n'ai pas de réputation désolée.
j0N45