【问题标题】:SQL Server stored procedure with subquery Optimization带有子查询优化的 SQL Server 存储过程
【发布时间】:2012-04-03 04:30:30
【问题描述】:

我有四张桌子:

  1. 销售人员 (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,..):每日销售数据。

为了保存每个销售人员的每日销售额,我首先需要显示他所有销售点的列表,其中包含每个销售点的总和(如果有)。

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

我有存储过程:

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 秒以上!

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

【问题讨论】:

  • @SalesPersonId IS NULL OR par.SalesPersonId = @SalesPersonId 对于您 传入特定 SalesPersonId 的情况可能不是最佳选择(至少除非您在 2008 年并使用 OPTION(RECOMPILE)) .表上有哪些索引以及执行计划是什么样的?
  • @Martin Smith 忘记了一个讨厌的参数嗅探错误

标签: sql sql-server optimization


【解决方案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 代码中重复 Inline 视图。

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 

【讨论】:

  • 我认为第一个选项不会解析,因为GROUPing 错误-expr1 不在GROUP BY 中。 ORDER BY 也将失败,因为 districtid 不在选择列表中。不过,ORDER BY 问题存在于 OP 的代码中。
  • @JNK doh。谢谢,如果我不使用 Instant SQL 格式化程序,我总是将 GROUP BY 子句放在那些上面。我还弄乱了一些逗号和括号。但是您可以通过不在选择中的东西来订购,只要它在 group by 中即可。 I wasn't sure myself so I tested it
  • @conrad..谢谢,我选择了第一个选项并进行了一些修改,加上(@JNK 提到的分组和排序),
  • 很高兴它现在可以工作(因为答案已被接受),但是您现在从中获得了什么样的表现+您还做了其他什么吗? (如添加索引)
  • @deroby- 谢谢,实际上我添加了索引 (soldamount,SalesDate),我接受 conrad 答案作为子查询的一个很好的更改,性能更好但仍然很低!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多