如何优化此动态存储过程

时间:2011-11-16 14:23:22

标签: sql sql-server-2008 stored-procedures

我有一个使用20个表检索数据的存储过程。 程序样本:

     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 ') 

一些事实:

  • 上述程序是我正在处理的存储程序的简单形式。

  • 我正在使用SQL Server 2008

  • 我的实际过程有15个参数,所有这些参数都在WHERE子句中使用。如果为参数提供了值,则该参数包含在WHERE子句中,否则不包含。

  • 至少有10列值来自GROUP BY条件,如上述程序中给出的“NumberOfVisits”。

  • 我在所有主键上都有索引。外键。

  • 我在WHERE子句中使用的所有列都有索引。

  • 我在GROUP BY子句中使用的所有列都有索引。

问题

  • Q1:这是按照上述模式创建动态存储过程的最佳做法吗?

  • Q2:我使用以下命令得到了此过程的输出SQL: - PRINT(@querySELECT +'WHERE'+ @queryWHERE + @queryExtraColumns + @queryReturnResults +'DROP TABLE #tempResults') 当我运行该SQL时,它占用了存储过程所用的相同时间,为什么? SQL不应该花费更少的时间吗?为什么没有区别?

  • 问题3:以上是获取汇总列值(“NumberOfVisits”)的最佳做法吗?

  • 问题4:以上是动态创建WHERE子句的最佳方法吗?

  • 问题5:在上述情况下,我可以通过使用替代方法来避免使用临时表吗?

  • 问题6:如何优化此程序?

如果我的问题不明确或不合适,请原谅我。

感谢您宝贵的时间和帮助

2 个答案:

答案 0 :(得分:1)

对于动态报告,最好设置SSRS(SQL Server Reporting Services),它是SQL Server 2005到2008 R2的一部分。然后使用BIDS创建一个数据模型项目来运行Report Builder。报表生成器附带SSRS,是一次性点击应用程序。

在这个时代,尝试通过本土查询跟上动态报告只是不再有效。

答案 1 :(得分:0)

同样的问题也被我发布在另一个网站上。 Grant Fritchey回答了这个问题 你可以看到答案here 我复制了该网站的答案,见下文:

  

通常,不是构建字符串并执行它,而是可以   受到SQL注入攻击,我建议建立一个字符串   使用参数并使用sp_executesql来执行它,提供   参数。这是更多的工作,但它更安全,而且,它更多   可能会看到改进的计划重用。

     

不,查询是执行的很长一段时间。建立一些   字符串非常无痛。

     

就汇总列而言,不,这是额外的通行证。相反,我   对子选择执行连接(通常,有时会破坏它)   分步可以更好地工作)。

     

不。往上看。 sp_executesql更好。

     

是的,请将其作为单一选择声明进行计划。临时表就是   持有聚合,可以作为派生表来完成。

     

优化?更严厉的问题。只要看看你有什么,CONVERT   关于e.EnquiryDate的陈述将导致扫描,无论如何。   如果这是一个datetime列,则需要将其与日期时间进行比较   价值,没有转换。除此之外,我没有看到没有看到   整个查询,执行计划,数据,结构,所有这些。

     

通常,这些捕获所有类型的查询都是非常有问题的。   而不是这样做,找出将要的常见模式   不可避免地存在,这三列总是进来,这只是一个   当另一个人进来等时进来,然后建立三个或四个   不同的过程考虑到这些模式和使用   这是一个包装器proc来确定它应该去哪些触发器   至。设置它将是一项工作,但不会超过这个,而且它会   表现更好,更容易维护。