2011-05-05 70 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