2011-11-16 5 views
1

J'ai une procédure stockée qui récupère des données en utilisant 20 tables. Exemple de la procédure:Comment optimiser cette procédure stockée dynamique

 CREATE PROCEDURE GetEnquiries 
    (
     @EnquiryDate DATETIME  = NULL 
    ) 
    AS 

    DECLARE @querySELECT   VARCHAR(MAX) = '' 
    DECLARE @queryWHERE   VARCHAR(MAX) = '' 
    DECLARE @queryExtraColumns  VARCHAR(MAX) = '' 
    DECLARE @queryReturnResults VARCHAR(MAX) = '' 

    ----------------------------------------------------- 
    --Create temp table 
    ----------------------------------------------------- 
    SET @querySELECT = ' 
       CREATE TABLE #tempResults 
       ( 
        EnquiryId  INT, 
        Cost   Decimal(18,2), 
        CustomerName VARCHAR(50), 
        EnquiryStatus VARCHAR(50), 
        ContactNumber VARCHAR(50), 
        NumberOfVisits INT 
       ) ' 

    ----------------------------------------------------- 
    --Insert into temp table 
    ----------------------------------------------------- 
    SET @querySELECT = '    
       INSERT INTO #tempResults 
       ( 
        EnquiryId  , 
        Cost   , 
        CustomerName , 
        EnquiryStatus , 
        ContactNumber 
       ) ' 
    ----------------------------------------------------- 
    --SELECT 
    ----------------------------------------------------- 
    SET @querySELECT = ' 
      SELECT   
        e.EnquiryId  , 
        e.Cost   , 
        c.CustomerName , 
        e.EnquiryStatus , 
        c.ContactNumber 
      FROM Enquiry e 
        INNER JOIN Customers c ON e.CustomerId = c.CustomerId ' 

    ----------------------------------------------------- 
    -- WHERE 
    ----------------------------------------------------- 
    IF(@EnquiryDate IS NOT NULL) 
     BEGIN 
      SET @queryWHERE = @queryWHERE + ' CONVERT(VARCHAR(10),e.EnquiryDate,23) >= ' + ''''+ CONVERT(VARCHAR(10),@EnquiryDate,23) + '''' 
     END 

    --- There are at least 14 parameters used in WHERE operation the above is just one of them 
    ----------------------------------------------------- 
    -- Count NumberOfVisits 
    ----------------------------------------------------- 
     SET @queryExtraColumns = ' 
      ;WITH NumberOfVisits AS 
      (
       SELECT t.EnquiryId, COUNT(EnquiryId) AS NumberOfVisits 
       FROM NumberOfVisits v 
         INNER JOIN #tempResults t ON v.EnquiryId = t.EnquiryId 
       GROUP BY t.EnquiryId 
      ) 


     UPDATE #tempResults 
     SET  NumberOfVisits = u.NumberOfVisits 
     FROM #tempResults t 
       INNER JOIN NumberOfVisits u ON u.EnquiryId = t.EnquiryId 

' 

    ----------------------------------------------------- 
    -- return the results 
    -----------------------------------------------------  
    SET @queryReturnResults = ' 
      SELECT   
        EnquiryId  , 
        Cost   , 
        CustomerName , 
        EnquiryStatus , 
        ContactNumber , 
        NumberOfVisits 
      FROM #tempResults t 
        ' 

    ----------------------------------------------------- 
    -- Combine all the strings + DROP the temp table 
    ----------------------------------------------------- 
    -- PRINT( @querySELECT + ' WHERE ' + @queryWHERE + @queryExtraColumns + @queryReturnResults + ' DROP TABLE #tempResults ') 
     EXEC(@querySELECT + ' WHERE ' + @queryWHERE + @queryExtraColumns + @queryReturnResults + ' DROP TABLE #tempResults ') 

Quelques faits:

  • La procédure ci-dessus est la forme simple de la procédure stockée je travaille.

  • J'utilise SQL Server 2008

  • Ma procédure actuelle a 15 paramètres, tous sont utilisés dans la clause WHERE. Si la valeur est fournie pour un paramètre, le paramètre est inclus dans la clause WHERE sinon.

  • Il y a au moins 10 colonnes dont la valeur provient de la condition GROUP BY comme celle "NumberOfVisits" donnée dans la procédure ci-dessus.

  • J'ai des index sur toutes les clés primaires & Clés étrangères.

  • Je index sur les toutes les colonnes qui sont utilisées dans la clause WHERE.

  • Je index sur les toutes les colonnes qui sont utilisées dans la clause GROUP BY.

Questions:

  • Q1: Est-ce est selon les meilleures pratiques pour créer des procédures stockées dynamiques suivant modèle ci-dessus?

  • Q2: Je suis le SQL de sortie de cette procédure en utilisant: - IMPRIMER (@querySELECT + '+' OÙ @queryWHERE + @queryExtraColumns + @queryReturnResults + 'DROP TABLE #tempResults') quand je cours que SQL a pris le même temps qui a été pris par la procédure stockée, pourquoi? n'est pas le SQL devrait prendre moins de temps? pourquoi il n'y a pas de différence?

  • Q3: Est-ce ce qui précède est la meilleure pratique pour obtenir la valeur des colonnes de synthèse (les « NumberOfVisits »)?

  • Q4: est ce qui précède est la meilleure façon de créer la clause WHERE dynamique?

  • Q5: Puis-je éviter l'utilisation de la table temporaire en utilisant une autre dans le scénario ci-dessus?

  • Q6: Que puis-je faire pour optimiser cette procédure?

S'il vous plaît pardonnez-moi, si ma question n'est pas claire ou pas une bonne question.

Merci pour votre temps précieux & aide.

+3

[La malédiction et les bénédictions du SQL dynamique] (http://www.sommarskog.se/dynamic_sql.html) – Oded

Répondre

0

Cette question a été aussi postée par un autre sur un autre site. Cette question a été répondu par Grant Fritchey Vous pouvez voir la réponse here J'ai copié la réponse de ce site, voir ci-dessous:

En général, au lieu de construire une chaîne et son exécution, qui peut être soumis à Les attaques par injection SQL, je suggère de construire une chaîne avec des paramètres et en utilisant sp_executesql pour l'exécuter, fournissant les paramètres . C'est plus de travail, mais c'est plus sûr, ET, il est plus probable de voir une meilleure réutilisation du plan.

Non, la requête est la partie longue de l'exécution. Construire quelques chaînes est assez sans douleur.

En termes de colonnes de résumé, non, c'est un passage supplémentaire. Au lieu de cela, j'effectuerais une jointure avec un sous-select (en général, parfois le décomposer en étapes peut mieux fonctionner).

Non. Voir au dessus. sp_executesql est meilleur.

Ouais, planifiez-le comme une seule instruction select. La table temporaire est juste en tenant pour l'agrégation qui peut être faite comme une table dérivée.

Optimiser? Question plus difficile. Juste voir ce que vous avez, la déclaration CONVERT sur e.EnquiryDate va provoquer une analyse, peu importe quoi. S'il s'agit d'une colonne datetime, vous devez la comparer à une valeur datetime , sans conversions. Au-delà de ça, je ne pourrais pas dire sans voir les requêtes entières, les plans d'exécution, les données, la structure, tout ça.

En général, ces requêtes catch tout type sont extrêmement problématiques. Au lieu de cela, identifier les modèles communs qui vont existent inévitablement, ces trois colonnes entrent toujours, celui-ci seulement entre quand un autre entre, etc, et ensuite construire trois ou quatre différents procs qui prennent prendre tenir compte de ces modèles et utiliser comme un wrapper proc pour déterminer lequel de ces processus il devrait aller . Ce sera un travail à mettre en place, mais pas plus que cela, et il sera plus performant et sera plus facile à maintenir.

1

Pour rapports dynamiques, vous êtes mieux la mise en place SSRS (SQL Server Reporting Services), qui est une partie de SQL Server 2005 à 2008 R2.Utilisez ensuite BIDS pour créer un projet de modèle de données pour exécuter le Générateur de rapports. Le Générateur de rapports est fourni avec SSRS et est une application à cliquer une seule fois.

De nos jours, essayer de suivre les rapports dynamiques via des requêtes locales n'est plus aussi efficace.

Questions connexes