2011-05-05 118 views
2

因此,我繼承了someones產品,並且在查看大量代碼時,我感覺很多方面都可以改進。我的第一個任務是,我希望有人能夠讓我走上正確的軌道,優化下面的存儲過程。雖然我很綠,但我不禁覺得必須有更好的方法來做到這一點......它需要4分鐘以上的時間才能完成。帶有多個相似子查詢的存儲過程的TSQL優化

在存儲過程中,存在多次相同的連接。我真的不是要求別人來做我的工作,但請有人給我一個開始,以便如何更好地構造以下內容?:

我應該創建臨時表而不是做這麼多嵌套連接嗎?

感謝,

BEGIN 

DECLARE @District VARCHAR(50) 
SET @District = '42' 

    SET NOCOUNT ON; 
    DECLARE @today varchar(30) 
    DECLARE @ToDatestr varchar(20) 
    DECLARE @ToDate15 varchar(20) 
    DECLARE @BOYear varchar(30) 
    DECLARE @BOMonth varchar(30) 
    DECLARE @BOWeek varchar(30) 
    SET @today = RIGHT('00'+CAST(MONTH(getdate()) as varchar), 2) + '/' + RIGHT('00'+CAST(DAY(getdate()) as varchar), 2) + '/' + CAST(YEAR(getdate()) as varchar) 
    SELECT d.utilitydistrictnumber AS "District #", 
      emr.ExistingMeterID, 
      emr.isvc AS "ISVC#", 
      r.Utilityrouteid AS "Utility Route #", 
      emr.cyclenumber AS "Utility Cycle #", 
      pd."Name", 
      REPLACE(REPLACE(pd."Address",CHAR(10),''),',',';') AS 'Address', 
      CONVERT(float,(CASE WHEN ISNULL(p.Latitude,'.000000') = '.000000' THEN dw_p.Lat ELSE p.Latitude END)) AS 'Latitude', 
      CONVERT(float,(CASE WHEN ISNULL(p.Longitude,'.000000') = '.000000' THEN dw_p.Long ELSE p.Longitude END)) AS 'Longitude', 
      WeekendCustContact.mCount AS 'Weekend CustContact', 
      After5PMCustContact.mCount AS 'After 5PM CustContact', 
      TotalCustContact.mCount AS 'Total CustContact', 
      AppointmentArranged.mCount AS 'Appointment Arranged', 
      FieldUTC.mCount AS 'Total FieldUTCs', 
      Letters.TotalHTALetter , 
      emr.UtilityOnHold, 
      emr.DeploymentOnHold, 
      emr.DeploymentOnHoldReason, 
      ,o.ActivityName 
     From Product_CompanyProd_Repository.dbo.Existingmetersroutes emr (NOLOCK) 
     INNER JOIN 
     Product_CompanyProd_Repository.dbo.ExistingmetersPremises emp (NOLOCK) 
       ON emp.existingmeterid = emr.existingmeterid 
     INNER JOIN 
     Product_CompanyProd_Repository.dbo.Premises p (NOLOCK) 
       ON p.premiseid = emp.premiseid 
     LEFT JOIN 
     [ProductMAIN-ALIAS].[DW_Company].[dbo].[Premise_LatLongs] dw_p (NOLOCK) 
       ON dw_p.premiseid = p.premiseid 
     INNER JOIN 
     [Product_CompanyPROD_Repository].[dbo].[routes] AS r  (NOLOCK) 
      ON r.routeid = emr.routeid 
     INNER JOIN 
     [Product_CompanyPROD_Repository].[dbo].[Districts] AS d (NOLOCK) 
      ON d.districtid = r.districtid AND d.utilitydistrictnumber = @District 
     LEFT JOIN [Product_CompanyProd].[dbo].[ODMorders] o 
      ON o.summary = emr.isvc AND o.StatusID < 9 
     LEFT JOIN 
      (SELECT oo.Summary AS ISVC, COUNT(*) AS mcount 
        FROM Product_CompanyProd.dbo.ODMOrders AS oo (NOLOCK) 
         INNER JOIN 
         Product_CompanyProd.dbo.ODMOrderAttributesUME AS oa (NOLOCK) 
           ON oa.Orderid = oo.Orderid AND oa.UTCCode <> '' 
            AND oa.district = @District 
        WHERE oo.StatusID IN (9,10) 
        GROUP BY oo.summary 
      ) AS FieldUTC ON FieldUTC.isvc=emr.isvc 
     LEFT JOIN 
      (SELECT e.isvc, COUNT(*) AS mcount 
       FROM [Product_CompanyProd_Repository].[dbo].[existingmetersroutes] e (NOLOCK) 
       INNER JOIN 
       [Product_CompanyProd_Repository].[dbo].[existingmeterspremises] p (NOLOCK) 
        on e.existingmeterid = p.existingmeterid 
       INNER JOIN 
       [Product_CompanyProd_Repository].[dbo].[premisenotes] pn (NOLOCK) 
        on pn.premiseid = p.premiseid 
         AND DATEPART(dw, pn.autotimestamp) IN (7,1) 
       WHERE category = 'Call attempt' 
       GROUP BY e.isvc 

      ) AS WeekendCustContact ON WeekendCustContact.isvc=emr.isvc 
     LEFT JOIN 
      (SELECT e.isvc, COUNT(*) AS mcount 
       FROM [Product_CompanyProd_Repository].[dbo].[existingmetersroutes] e (NOLOCK) 
       INNER JOIN 
       [Product_CompanyProd_Repository].[dbo].[existingmeterspremises] p (NOLOCK) 
        on e.existingmeterid = p.existingmeterid 
       INNER JOIN 
       [Product_CompanyProd_Repository].[dbo].[premisenotes] pn (NOLOCK) 
        on pn.premiseid = p.premiseid 
         AND datepart(hh,pn.autotimestamp) >= 17 
       WHERE category = 'Call attempt' 
       GROUP BY e.isvc 

      ) AS "After5PMCustContact" ON After5PMCustContact.isvc=emr.isvc 
     LEFT JOIN 
      (SELECT e.isvc, COUNT(*) AS mcount 
       FROM [Product_CompanyProd_Repository].[dbo].[existingmetersroutes] e (NOLOCK) 
       INNER JOIN 
       [Product_CompanyProd_Repository].[dbo].[existingmeterspremises] p (NOLOCK) 
        on e.existingmeterid = p.existingmeterid 
       INNER JOIN 
       [Product_CompanyProd_Repository].[dbo].[premisenotes] pn (NOLOCK) 
        on pn.premiseid = p.premiseid 
       WHERE category IN ('Call attempt','Door hanger','Letter received by customer','Call to Company who referred the caller to OurCompany') 
       GROUP BY e.isvc 

      ) AS "TotalCustContact" ON TotalCustContact.isvc=emr.isvc 
     LEFT JOIN 
      (SELECT oo.Summary AS ISVC, COUNT(*) AS mcount 
        FROM Product_CompanyProd.dbo.ODMOrders AS oo (NOLOCK) 
        WHERE oo.ActivityName = 'CompanyExchangeAppt' AND oo.StatusID < 9 
        GROUP BY oo.summary 
      ) AS "AppointmentArranged" ON AppointmentArranged.isvc=emr.isvc 
     LEFT JOIN 

      (SELECT emr.ISVC,ema.ColumnValue AS "TotalHTALetter" 
      FROM 
       Product_CompanyProd_Repository.dbo.Existingmetersroutes emr (NOLOCK) 
       INNER JOIN 
       Product_CompanyProd_Repository.dbo.ExistingmetersAuxiliary ema (NOLOCK) on ema.existingmeterid = emr.existingmeterid 
        AND ema.ColumnName LIKE 'HTALetter%' 
      ) AS "Letters" ON Letters.isvc=emr.isvc 

     LEFT JOIN 
     (SELECT * FROM 
     (SELECT o.summary AS isvc, 
      REPLACE(REPLACE([od].Information.query('data(OrderDetails/PremiseDetails/Name)').value('.','varchar(50)'),CHAR(10),''),',',';') AS "Name", 
      REPLACE(REPLACE([od].Information.query('data(OrderDetails/Location/StreetAddress)').value('.','varchar(50)'),CHAR(10),''),',',';') AS "Address", 
      [od].Information.query('data(OrderDetails/PremiseDetails/Phone)').value('.','varchar(50)') AS "Phone", 
      o.Autotimestamp 
      From Product_CompanyProd.dbo.ODMOrders AS o (NOLOCK) 
       INNER JOIN 
       Product_CompanyProd.dbo.ODMOrderAttributesUME AS oa (NOLOCK) 
        ON oa.Orderid = o.Orderid AND oa.district = @District AND oa.UTCCode <> '' 
       INNER JOIN 
       [Product_CompanyProd].[dbo].[ODMOrderdetails] od (NOLOCK) 
        ON od.Orderid = o.Orderid 
       WHERE o.StatusID IN (9,10) 
     ) AS pd 
     WHERE 
       pd.Autotimestamp=(SELECT MAX(o.autotimestamp) 
            From Product_CompanyProd.dbo.ODMOrders AS o (NOLOCK) 
             INNER JOIN 
             Product_CompanyProd.dbo.ODMOrderAttributesUME AS oa (NOLOCK) 
              ON oa.Orderid = o.Orderid AND oa.district = @District 
             INNER JOIN 
             [Product_CompanyProd].[dbo].[ODMOrderdetails] od (NOLOCK) 
              ON od.Orderid = o.Orderid 
            WHERE o.summary = pd.isvc AND 
              o.StatusID IN (9,10) 
            ) 
     ) AS pd ON pd.isvc = emr.isvc 

     Where 
      emr.Status NOT IN ('Complete','Fieldcomplete','UTC') 

END 
+0

什麼版本的SQL Server – 2011-05-05 17:37:36

+0

很難在不知道的主鍵,有哪些指數,這些表中的行數(數量級)以及不知道某些列的業務目的。 – cdonner 2011-05-05 17:50:04

+0

這是SQL服務器2005 – Kipper007 2011-05-05 18:53:51

回答

1

是的,看着是,它可以被優化。馬上蝙蝠,你可以行

SET @today = RIGHT('00'+CAST(MONTH(getdate()) as varchar), 2) + '/' + 
      RIGHT('00'+CAST(DAY(getdate()) as varchar), 2) + '/' + 
      CAST(YEAR(getdate()) as varchar) 

改變

SET @today = convert(varchar,GETDATE(),101) 

這使我相信還有其他事情可以做,以助陣演出。看着它讓我覺得他們正在嘗試構建一個數據透視表或矩陣報告,請嘗試使用PIVOT命令或CTE(公用表表達式)重建查詢。我會傾向於先嚐試CTE。
--Chris MSDN上

這些鏈接到SQL 2005所引用

Pivot Info

CTE

2

這3子查詢可以合併:

... 
LEFT JOIN 
    (SELECT e.isvc, COUNT(*) AS mcount 
     FROM [Product_CompanyProd_Repository].[dbo].[existingmetersroutes] e (NOLOCK) 
     INNER JOIN 
     [Product_CompanyProd_Repository].[dbo].[existingmeterspremises] p (NOLOCK) 
      on e.existingmeterid = p.existingmeterid 
     INNER JOIN 
     [Product_CompanyProd_Repository].[dbo].[premisenotes] pn (NOLOCK) 
      on pn.premiseid = p.premiseid 
       AND DATEPART(dw, pn.autotimestamp) IN (7,1) 
     WHERE category = 'Call attempt' 
     GROUP BY e.isvc 

    ) AS WeekendCustContact ON WeekendCustContact.isvc=emr.isvc 
LEFT JOIN 
    (SELECT e.isvc, COUNT(*) AS mcount 
     FROM [Product_CompanyProd_Repository].[dbo].[existingmetersroutes] e (NOLOCK) 
     INNER JOIN 
     [Product_CompanyProd_Repository].[dbo].[existingmeterspremises] p (NOLOCK) 
      on e.existingmeterid = p.existingmeterid 
     INNER JOIN 
     [Product_CompanyProd_Repository].[dbo].[premisenotes] pn (NOLOCK) 
      on pn.premiseid = p.premiseid 
       AND datepart(hh,pn.autotimestamp) >= 17 
     WHERE category = 'Call attempt' 
     GROUP BY e.isvc 

    ) AS "After5PMCustContact" ON After5PMCustContact.isvc=emr.isvc 
LEFT JOIN 
    (SELECT e.isvc, COUNT(*) AS mcount 
     FROM [Product_CompanyProd_Repository].[dbo].[existingmetersroutes] e (NOLOCK) 
     INNER JOIN 
     [Product_CompanyProd_Repository].[dbo].[existingmeterspremises] p (NOLOCK) 
      on e.existingmeterid = p.existingmeterid 
     INNER JOIN 
     [Product_CompanyProd_Repository].[dbo].[premisenotes] pn (NOLOCK) 
      on pn.premiseid = p.premiseid 
     WHERE category IN ('Call attempt','Door hanger','Letter received by customer','Call to Company who referred the caller to OurCompany') 
     GROUP BY e.isvc 

    ) AS "TotalCustContact" ON TotalCustContact.isvc=emr.isvc 
... 

這裏是一個可能的組合版本:

LEFT JOIN 
    (SELECT 
     e.isvc, 
     COUNT(*) AS TotalCount, 
     COUNT(CASE WHEN DATEPART(dw, pn.autotimestamp) IN (7, 1) AND category = 'Call attempt' THEN 1 END) AS WeekendCount, 
     COUNT(CASE WHEN datepart(hh, pn.autotimestamp) >= 17  AND category = 'Call attempt' THEN 1 END) AS After5PMCount 
    FROM [Product_CompanyProd_Repository].[dbo].[existingmetersroutes] e (NOLOCK) 
     INNER JOIN 
     [Product_CompanyProd_Repository].[dbo].[existingmeterspremises] p (NOLOCK) 
      on e.existingmeterid = p.existingmeterid 
     INNER JOIN 
     [Product_CompanyProd_Repository].[dbo].[premisenotes] pn (NOLOCK) 
      on pn.premiseid = p.premiseid 
     WHERE category IN ('Call attempt','Door hanger','Letter received by customer','Call to Company who referred the caller to OurCompany') 
     GROUP BY e.isvc 

    ) AS "CustContact" ON CustContact.isvc=emr.isvc 

當然,您還需要替換選擇列表中的相應列。

查詢的性能降低的另一個可能的原因是這個小怪物:

... 
LEFT JOIN 
(SELECT * FROM 
(SELECT o.summary AS isvc, 
    REPLACE(REPLACE([od].Information.query('data(OrderDetails/PremiseDetails/Name)').value('.','varchar(50)'),CHAR(10),''),',',';') AS "Name", 
    REPLACE(REPLACE([od].Information.query('data(OrderDetails/Location/StreetAddress)').value('.','varchar(50)'),CHAR(10),''),',',';') AS "Address", 
    [od].Information.query('data(OrderDetails/PremiseDetails/Phone)').value('.','varchar(50)') AS "Phone", 
    o.Autotimestamp 
    From Product_CompanyProd.dbo.ODMOrders AS o (NOLOCK) 
     INNER JOIN 
     Product_CompanyProd.dbo.ODMOrderAttributesUME AS oa (NOLOCK) 
      ON oa.Orderid = o.Orderid AND oa.district = @District AND oa.UTCCode <> '' 
     INNER JOIN 
     [Product_CompanyProd].[dbo].[ODMOrderdetails] od (NOLOCK) 
      ON od.Orderid = o.Orderid 
     WHERE o.StatusID IN (9,10) 
) AS pd 
WHERE 
     pd.Autotimestamp=(SELECT MAX(o.autotimestamp) 
          From Product_CompanyProd.dbo.ODMOrders AS o (NOLOCK) 
           INNER JOIN 
           Product_CompanyProd.dbo.ODMOrderAttributesUME AS oa (NOLOCK) 
            ON oa.Orderid = o.Orderid AND oa.district = @District 
           INNER JOIN 
           [Product_CompanyProd].[dbo].[ODMOrderdetails] od (NOLOCK) 
            ON od.Orderid = o.Orderid 
          WHERE o.summary = pd.isvc AND 
            o.StatusID IN (9,10) 
         ) 
) AS pd ON pd.isvc = emr.isvc 
... 

而這裏的我會怎樣把它改寫:

LEFT JOIN 
    (SELECT 
     o.summary AS isvc, 
     REPLACE(REPLACE([od].Information.query('data(OrderDetails/PremiseDetails/Name)').value('.','varchar(50)'),CHAR(10),''),',',';') AS "Name", 
     REPLACE(REPLACE([od].Information.query('data(OrderDetails/Location/StreetAddress)').value('.','varchar(50)'),CHAR(10),''),',',';') AS "Address", 
     [od].Information.query('data(OrderDetails/PremiseDetails/Phone)').value('.','varchar(50)') AS "Phone", 
     o.Autotimestamp 
    From Product_CompanyProd.dbo.ODMOrders AS o (NOLOCK) 
     INNER JOIN 
     Product_CompanyProd.dbo.ODMOrderAttributesUME AS oa (NOLOCK) 
      ON oa.Orderid = o.Orderid AND oa.district = @District AND oa.UTCCode <> '' 
     INNER JOIN 
     [Product_CompanyProd].[dbo].[ODMOrderdetails] od (NOLOCK) 
      ON od.Orderid = o.Orderid 
    WHERE o.StatusID IN (9,10) 
     AND NOT EXISTS (
     SELECT * 
     FROM Product_CompanyProd.dbo.ODMOrders o2 
      INNER JOIN Product_CompanyProd.dbo.ODMOrderAttributesUME AS oa2 (NOLOCK) 
      ON oa2.Orderid = o2.Orderid AND oa2.district = @District AND oa2.UTCCode <> '' 
     WHERE o.summary = o2.summary AND o2.StatusID IN (9,10) AND o.Autotimestamp < o2.Autotimestamp 
    ) 
    ) AS pd ON pd.isvc = emr.isvc 
+0

Andriy,這對我來說是一個很好的開始。非常感謝。 我會做出修改並讓你知道性能的外觀。 – Kipper007 2011-05-05 22:00:33

+0

@ Kipper007:不客氣。我注意到我的第二個查詢中有個錯誤。必須讓它變得更復雜一點。不過,我認爲它應該比原來更快。 – 2011-05-05 22:20:14

+0

@Andriy:嘿,再次,只是想知道你是否可以爲我解釋'怪物'的問題? 我獲得了大部分重組,但我不明白的是'NOT EXISTS'條款。你能告訴我它指的是什麼嗎?據我所知 ival不存在於(1,2,3,4) 但AND NOT EXISTS沒有引用特定列? 對不起,如此困惑! – Kipper007 2011-05-07 00:49:24