带有子查询优化的SQL Server存储过程

时间:2012-04-02 20:11:23

标签: sql sql-server optimization

我有四张桌子:

  1. SalesPerson personId int ,PersonName nvarchar(20) ..)
  2. personAndDistrict autoId int,PersonId int,districtId int):在特定地区工作的每个销售人员
  3. PointOfSales SalesId int,pointOfSalesId int,districtId int,pointOfSalesName nvarchar(20),...)每个区都有很多pointOfSales'。
  4. DailySales SalesPersonId int,SoldAmount float,PointOfSalesId int, salesDate date,..):每日销售数据。
  5. 为了保存每个销售人员的每日销售额,我首先需要显示其所有销售点的列表,其中每个销售额的总和(如果有)。

    因此,我们需要的是显示特定销售人员在特定日期的所有销售点以及总销售金额。

    我有存储过程:

    ALTER PROCEDURE [dbo].[SP_PoinOfSales_GetDataForEntry]
          @SalesPersonId   int=null, @SalesDate  date=null
    AS
       SELECT DISTINCT 
           par.SalesPersonId, 
           pos.pointOfsalesID, 
           pos.pointOfSalesName, 
           ISNULL((SELECT SUM(SoldAmount) AS Expr1 
                   FROM   
                      dbo.DailySales AS ds 
                   WHERE  
                      (SalesDate = CONVERT(VARCHAR, @salesDate, 102)) 
                      AND (SalesPersonId = par.SalesPersonId)  
                      AND (PointOfsalesID = ps.PointOfsalesID)), 0) AS SoldAmount, 
       FROM   
           dbo.PointOfSales AS pos 
       INNER JOIN 
           dbo.PersonAndDistrict AS par ON pos.districtId = par.districtId 
       WHERE  
           (@SalesPersonId IS NULL 
            OR par.SalesPersonId = @SalesPersonId ) 
       GROUP BY 
           pos.districtId, 
           pos.pointOfSalesName, 
           pos.PointOfsalesID, 
           par.SalesPersonId 
       ORDER BY 
           pos.PointOfsalesID, 
           pos.districtId 
    

    只返回800条记录(销售点)需要超过6秒!

    那么,如何优化此存储过程以在最短时间内执行?

1 个答案:

答案 0 :(得分:1)

通常,我发现JOIN中的内联视图比SELECT子句中的内联视图执行得更好。所以我会这样改写。

SELECT DISTINCT par.salespersonid, 
            pos.pointofsalesid, 
            pos.pointofsalesname, 
            Isnull(t.expr1, 0), 
            as soldamount 
FROM   dbo.pointofsales AS pos 
       INNER JOIN dbo.personanddistrict AS par 
         ON pos.districtid = par.districtid 
       LEFT JOIN (SELECT SUM(soldamount) AS expr1, 
                         pointofsalesid, 
                         salespersonid 
                  FROM   dbo.dailysales AS ds 
                  WHERE  ( salesdate = CONVERT(VARCHAR, @salesDate, 102) ) 
                  GROUP  BY pointofsalesid, 
                            salespersonid) t 
         ON t.salespersonid = par.salespersonid 
            AND t.pointofsalesid = pos.pointofsalesid 
WHERE  ( @SalesPersonId IS NULL 
          OR par.salespersonid = @SalesPersonId ) 
GROUP  BY pos.districtid, 
          pos.pointofsalesname, 
          pos.pointofsalesid, 
          par.salespersonid 
ORDER  BY pos.pointofsalesid, 
          pos.districtid 

另一种选择是转移到下面的CTE,但除非在SQL代码中重复内联视图,否则这主要是一种风格。

WITH t 
     AS (SELECT SUM(soldamount) AS expr1, 
                pointofsalesid, 
                salespersonid 
         FROM   dbo.dailysales AS ds 
         WHERE  ( salesdate = CONVERT(VARCHAR, @salesDate, 102) ) 
         GROUP  BY pointofsalesid, 
                   salespersonid) 
SELECT DISTINCT par.salespersonid, 
                pos.pointofsalesid, 
                pos.pointofsalesname, 
                Isnull(t.expr1, 0) AS soldamount, 
FROM   dbo.pointofsales AS pos 
       INNER JOIN dbo.personanddistrict AS par 
         ON pos.districtid = par.districtid 
       LEFT JOIN t 
         ON t.salespersonid = par.salespersonid 
            AND t.pointofsalesid = pos.pointofsalesid 
WHERE  ( @SalesPersonId IS NULL 
          OR par.salespersonid = @SalesPersonId ) 
GROUP  BY pos.districtid, 
          pos.pointofsalesname, 
          pos.pointofsalesid, 
          par.salespersonid 
ORDER  BY pos.pointofsalesid, 
          pos.districtid