2011-11-16 44 views
1

我有一个存储过程使用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 + '其中' + @queryWHERE + @queryExtraColumns + @queryReturnResults + 'DROP TABLE #tempResults')当我运行 那SQL所用的时间与存储过程所花费的时间相同,为什么?不是SQL应该花更少的时间?为什么没有区别?

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

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

  • 问题5:我可以通过在上面的场景中使用一些替代方法来避免使用临时表吗?

  • Q6:我能做些什么来优化这个程序?

请原谅我,如果我的问题不清楚或不是一个适当的问题。

感谢您宝贵的时间&帮助。

+3

[动态SQL的诅咒和祝福](http://www.sommarskog.se/dynamic_sql.html) – Oded

回答

0

同样的问题也发布在另一个网站上。对此问题予以Grant Fritchey 回答您可以看到here 我已经从该网站复制的答案回答,见下图:

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

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

根据汇总栏,不,这是一个额外的通行证。相反,我会 针对子选择执行连接(通常,有时将其拆分为步骤可以更好地工作)。

没有。往上看。 sp_executesql更好。

是的,计划出来作为一个选择语句。临时表只是 持有的聚合可以作为派生表完成。

优化?更难的问题。只要看看你有什么,无论如何,e.EnquiryDate上的CONVERT 声明都会导致扫描。 如果这是一个日期时间列,则需要将其与日期时间 的值进行比较,无需转换。除此之外,我不能说没有看到整个查询,执行计划,数据,结构等等。

通常,这些捕获所有类型的查询都是非常有问题的。 而不是这样做,确定将不可避免地存在的常见模式,这三列总是进来,这一个只有 进来时,另一个进来,等等,然后建立三个或四个 不同的过程采取考虑到这些模式,并使用 这个作为包装程序来确定哪些特效应该去 。这将是设置的工作,但不会超过这个,它会更好地运行,并且会更容易维护。

1

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

在这个时代,试图通过本土化查询跟上动态报告的效率已经不再那么高效了。