是否可以改进此存储过程

时间:2012-05-02 11:22:01

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

我有一个存储过程虽然工作正常,但使用了大量临时表,因此性能下降。是否有可能改进/重写它(可能使用CTE或其他方式来提高性能)

CREATE PROCEDURE [dbo].[PORT_GetFutureOpportunities] 
    -- Add the parameters for the stored procedure here
    (
        @SiebelAccId VARCHAR(50),
        @FromDate DATETIME,
        @ToDate DATETIME,
        @FilterCriteria INT,
        @AutoRenewalChk INT
    )
AS
BEGIN 

        DECLARE @FDate DATETIME, @TDate DATETIME 
        SET @FDate = DATEADD(day, DATEDIFF(day, 0, @FromDate), 0)
        SET @TDate = DATEADD(day, DATEDIFF(day, 0, @ToDate), 0)


        SELECT  DISTINCT e.nai_grant_number
        INTO #temp
        FROM smbecommerce..SalesItem si 
        INNER JOIN smbecommerce..sales s ON s.sales_id = si.sales_id
        INNER JOIN siebelextract..entitlement e ON SUBSTRING(e.nai_grant_number, 1,  NULLIF(CHARINDEX('-', e.nai_grant_number) - 1, -1)) = CAST (s.sales_order_id AS VARCHAR)        
        WHERE  si.auto_renewal_flag = (CASE WHEN @AutoRenewalChk = 1 THEN 1 END )
        OR si.auto_renewal_flag <= ( CASE WHEN @AutoRenewalChk = 0 THEN 1 END)

            --Creating Main query
           SELECT 
           e.nai_agreement_account_name AS [CompanyName]
          ,c.first_name  + ' '  + c.last_name AS [ContactName]
          ,c.work_phone AS [ContactPhone]
          ,c.email_address AS [EmailAddress]
          ,e.entitlement_end_date AS [ExpirationDate]
          ,e.nai_grant_number AS [GrantNumber]
          ,e.nai_quantity AS [Quantity]
          ,e.product_name AS [SkuDescription]              
          ,(CASE WHEN LEN(e.nai_superceded_id) > 0 THEN 1 ELSE 0 END)  AS [IsRenewed]

          INTO #temp1                  
          FROM siebelextract..entitlement e     
          INNER JOIN siebelextract..account a ON a.row_id = e.nai_reseller_id           
          INNER JOIN SiebelExtract.[dbo].contact c WITH (NOLOCK) ON e.nai_primary_contact_id = c.row_id 
          WHERE a.parent_account_id = @SiebelAccId
          AND LEN(E.nai_reason_code) = 0
          AND EXISTS (SELECT 1 FROM smbecommerce..renewalskus rs WHERE RS.sku = E.product_name)
          AND e.entitlement_end_date 
          BETWEEN @FDate         
          AND @TDate   

          IF (@AutoRenewalChk = 0 OR @AutoRenewalChk = 1)
          BEGIN 
                  CREATE TABLE #temp2(
                                       CompanyName VARCHAR(200)
                                      ,ContactName VARCHAR(200)
                                      ,ContactPhone VARCHAR(200)
                                      ,EmailAddress VARCHAR(200)
                                      ,ExpirationDate DATETIME
                                      ,GrantNumber VARCHAR(200)
                                      ,Quantity INT
                                      ,SkuDescription VARCHAR(200)
                                      ,IsRenewed INT
                                     ) 
                 IF @AutoRenewalChk = 0
                 BEGIN  
                 INSERT INTO #temp2 select #temp1.* FROM #temp1       
                 END
                 IF @AutoRenewalChk = 1
                 BEGIN     
                 INSERT INTO #temp2 SELECT * FROM #temp1  WHERE #temp1.[GrantNumber] NOT IN(SELECT t1.nai_grant_number FROM #temp t1)     
                 END                       
                 END    

                IF @FilterCriteria = 0
                BEGIN 
                    SELECT te.[CompanyName],te.[ContactName],te.[EmailAddress],te.[ContactPhone],te.[SkuDescription],te.[Quantity],te.[GrantNumber],te.[ExpirationDate],te.[IsRenewed] FROM #temp2 te      
                END

                IF @FilterCriteria = 1
                BEGIN 
                    SELECT te.[CompanyName],te.[ContactName],te.[EmailAddress],te.[ContactPhone],te.[SkuDescription],te.[Quantity],te.[GrantNumber],te.[ExpirationDate],te.[IsRenewed]   FROM #temp2 te
                    WHERE te.[IsRenewed] > 0
                END         

                IF @FilterCriteria = 2
                BEGIN 
                  SELECT te.[CompanyName],te.[ContactName],te.[EmailAddress],te.[ContactPhone],te.[SkuDescription],te.[Quantity],te.[GrantNumber],te.[ExpirationDate],te.[IsRenewed] FROM #temp2 te
                  WHERE te.[IsRenewed] = 0
                END
                DROP TABLE #temp2
                DROP TABLE #temp1
                DROP TABLE #temp

END

GO

由于

2 个答案:

答案 0 :(得分:1)

  • 首先识别代码。理解它是一团糟。
  • 临时表太多了。你加载temp,加载temp1,加载temp2 temp1的副本比使用更多的temp1行加载temp2。你需要 加入这些表格的方法。
  • 还有一个大错误。如果@AutoRenewalChk是2,那么临时2不会 已创建,但将由查询访问。那会失败。
  • 最后,这可能无法提高性能,但会产生更清洁 代码,而不是每个可能性的IF @FilterCriteria,运行查询并在where子句
  • 上添加CASE

答案 1 :(得分:1)

我已经压缩了你的存储过程。当然,我没有你的数据库所以我的SSMS看起来像血河之战,但根据你提供的代码,这应该工作。看看,请以任何方式告诉我。

我先粘贴代码,然后解释一下我做了什么。

CREATE PROCEDURE dbo.PORT_GetFutureOpportunities (
    @SiebelAccId VARCHAR(50),
    @FromDate DATETIME,
    @ToDate DATETIME,
    @FilterCriteria INT,
    @AutoRenewalChk INT
)
AS
BEGIN 
    DECLARE @FDate DATETIME, @TDate DATETIME 
    SET @FDate = CAST(@FromDate AS DATE)
    SET @TDate = CAST(@ToDate AS DATE)

    CREATE TABLE #temp(
        CompanyName VARCHAR(200),
        ContactName VARCHAR(200),
        ContactPhone VARCHAR(200),
        EmailAddress VARCHAR(200),
        ExpirationDate DATETIME,
        GrantNumber VARCHAR(200),
        Quantity INT,
        SkuDescription VARCHAR(200),
        IsRenewed INT
    ) 

    WITH cte AS (SELECT DISTINCT e.nai_grant_number    FROM smbecommerce..SalesItem si 
            INNER JOIN smbecommerce..sales s ON s.sales_id = si.sales_id
            INNER JOIN siebelextract..entitlement e ON SUBSTRING(e.nai_grant_number, 1,  NULLIF(CHARINDEX('-', e.nai_grant_number) - 1, -1)) = CAST (s.sales_order_id AS VARCHAR)        
        WHERE  si.auto_renewal_flag = (CASE WHEN @AutoRenewalChk = 1 THEN 1 END)
            OR si.auto_renewal_flag <= (CASE WHEN @AutoRenewalChk = 0 THEN 1 END)),
        rs AS (SELECT DISTINCT sku FROM smbecommerce..renewalskus)
    INSERT INTO #temp
    SELECT e.nai_agreement_account_name, c.first_name  + ' '  + c.last_name, c.work_phone, c.email_address, e.entitlement_end_date,
        e.nai_grant_number, e.nai_quantity, e.product_name, (CASE WHEN LEN(e.nai_superceded_id) > 0 THEN 1 ELSE 0 END)
    FROM siebelextract..entitlement e     
        INNER JOIN siebelextract..account a ON a.row_id = e.nai_reseller_id           
        INNER JOIN SiebelExtract.dbo.contact c WITH (NOLOCK) ON e.nai_primary_contact_id = c.row_id 
        INNER JOIN rs ON rs.sku = e.product_name
        LEFT JOIN cte ON e.nai_grant_number = cte.nai_grant_number
    WHERE a.parent_account_id = @SiebelAccId
    AND LEN(E.nai_reason_code) = 0
    AND e.entitlement_end_date BETWEEN @FDate AND @TDate   
    AND (@AutoRenewalChk = 0 OR cte.nai_grant_number IS NOT NULL)

    SELECT CompanyName, ContactName, EmailAddress, ContactPhone, SkuDescription, Quantity, GrantNumber, ExpirationDate, IsRenewed 
    FROM #temp
    WHERE (@FilterCriteria = 0)
    OR IsRenewed = (1 - (@FilterCriteria -1))

    DROP TABLE #temp
END
  1. 由于您使用的是SQL2008,因此您可以使用DATE类型。施放到DATE使你的意图比DATEADD(DATEDIFF))方法更清晰(我喜欢这种方式 - 剥离时间的新方法)。
  2. 将#Temp2定义移至顶部并将其更改为#Temp。此过程不需要多个临时表。
  3. 将原始#Temp更改为单列CTE(名为cte)。还有一个子选择会从你的sku表中降低性能,所以这也被转移到了一个名为rs的cte中。
  4. 将SELECT INTO#temp1更改为插入#Temp。 rs公用表表达式的内部联接以与以前相同的方式过滤结果,但这会更快。
  5. 整个IF (@AutoRenewalChk = 0 OR @AutoRenewalChk = 1)块已被查询中的两行替换:左边连接cte,插入语句的最后一行。
  6. @FilterCriteria检查已经压缩并包含在最终的select语句中。根据您的代码,如果@FilterCriteria为0,则返回所有行。否则,返回行,其中IsRenewed = 1且@FilterCriteria = 1,或IsRenewed = 0且@FilterCriteria = 2.
  7. 事实上,由于我们在这里所做的就是将数据插入到表中然后选择它,我们可以将其进一步压缩到单个select语句中:

    CREATE PROCEDURE dbo.PORT_GetFutureOpportunities (
        @SiebelAccId VARCHAR(50),
        @FromDate DATETIME,
        @ToDate DATETIME,
        @FilterCriteria INT,
        @AutoRenewalChk INT
    )
    AS
    BEGIN 
        DECLARE @FDate DATETIME, @TDate DATETIME 
        SET @FDate = CAST(@FromDate AS DATE)
        SET @TDate = CAST(@ToDate AS DATE)
    
        WITH cte AS (SELECT DISTINCT e.nai_grant_number    FROM smbecommerce..SalesItem si 
                INNER JOIN smbecommerce..sales s ON s.sales_id = si.sales_id
                INNER JOIN siebelextract..entitlement e ON SUBSTRING(e.nai_grant_number, 1,  NULLIF(CHARINDEX('-', e.nai_grant_number) - 1, -1)) = CAST (s.sales_order_id AS VARCHAR)        
            WHERE  si.auto_renewal_flag = (CASE WHEN @AutoRenewalChk = 1 THEN 1 END)
                OR si.auto_renewal_flag <= (CASE WHEN @AutoRenewalChk = 0 THEN 1 END)),
            rs AS (SELECT DISTINCT sku FROM smbecommerce..renewalskus)
        SELECT e.nai_agreement_account_name AS [CompanyName], c.first_name  + ' '  + c.last_name AS [ContactName],
            c.work_phone AS [ContactPhone], c.email_address AS [EmailAddress], e.entitlement_end_date AS [ExpirationDate],
            e.nai_grant_number AS [GrantNumber], e.nai_quantity AS [Quantity], e.product_name AS [SkuDescription],
            (CASE WHEN LEN(e.nai_superceded_id) > 0 THEN 1 ELSE 0 END)  AS [IsRenewed]
        FROM siebelextract..entitlement e     
            INNER JOIN siebelextract..account a ON a.row_id = e.nai_reseller_id           
            INNER JOIN SiebelExtract.dbo.contact c WITH (NOLOCK) ON e.nai_primary_contact_id = c.row_id 
            INNER JOIN rs ON rs.sku = e.product_name
            LEFT JOIN cte ON e.nai_grant_number = cte.nai_grant_number
        WHERE a.parent_account_id = @SiebelAccId
        AND LEN(E.nai_reason_code) = 0
        AND e.entitlement_end_date BETWEEN @FDate AND @TDate   
        AND (@AutoRenewalChk = 0 OR cte.nai_grant_number IS NOT NULL)
        AND ((@FilterCriteria = 0) OR CASE WHEN LEN(e.nai_superceded_id) > 0 THEN 1 ELSE 0 END = (1 - (@FilterCriteria -1)))
    END