0

我的问题是下面的查询需要 38 秒才能完成,我需要尽可能减少这个时间。当我查看执行计划时:Dim_Customers 索引扫描上的 %54 成本支出。任何建议将不胜感激。谢谢

DECLARE @SalesPersonCode NVARCHAR(4)
DECLARE @StartDate       DATETIME
DECLARE @EndDate         DATETIME

SET @SalesPersonCode = 'AC';
SET @StartDate       = '03/01/2012';
SET @endDate         = '03/31/2012';

SELECT AA_FactSalesOrderDetails.Salesperson
             , Dim_SalesOrganisation.[Salesperson name]
             , AA_FactSalesOrderDetails.[Order Date]
             , Dim_Customers.[Customer number]
             , Dim_Customers.[Customer name]
             , Dim_Customers.[Area/state]
             , Dim_Customers.country
             , Dim_Customers.[Customer stop] AS [Customer Block]
             , AA_FactSalesOrderDetails.[Customer order stop] AS [Co Stop]
             , AA_FactSalesOrderDetails.[First delivery date Header]
             , AA_FactSalesOrderDetails.[Last delivery date Header]
             , Dim_Customers.[User-defined field 6 - customer]
             , Dim_Customers.[Customer group name]
             , AA_FactSalesOrderDetails.[Contact Method]
             , AA_FactSalesOrderDetails.[Customer order number]
             , AA_FactSalesOrderDetails.[Price Level]
             , AA_FactSalesOrderDetails.[Item number]
             , Dim_Items.[Product group description]  AS [Item name]
             , AA_FactSalesOrderDetails.[Ordered quantity - basic U/M] AS [Quantity Ordered]
             , AA_FactSalesOrderDetails.[Ordered quantity - basic U/M] * AA_FactSalesOrderDetails.[Net price] AS [Order Line Total ]

FROM AA_FactSalesOrderDetails 
     LEFT JOIN
     Dim_SalesOrganisation
     ON
     AA_FactSalesOrderDetails.Salesperson = Dim_SalesOrganisation.Salesperson
     LEFT JOIN
     Dim_Customers
     ON
     AA_FactSalesOrderDetails.Dim_Customers_dKey = Dim_Customers.Dim_Customers_dKey
     LEFT JOIN
     Dim_Items
     ON
     AA_FactSalesOrderDetails.[Item number] = Dim_Items.[Item number]
     LEFT JOIN
     Dim_CustomerOrderTypes
     ON
     AA_FactSalesOrderDetails.[Customer order type] = Dim_CustomerOrderTypes.[Customer order type]



WHERE AA_FactSalesOrderDetails.[Order Date] 
      BETWEEN
      dbo.fnc_M3_sql_datetime_to_M3_date(@StartDate)    /* !!!Procedural Approach!!! */
      AND
      dbo.fnc_M3_sql_datetime_to_M3_date(@EndDate)      /* !!!Procedural Approach!!! */
      AND
      AA_FactSalesOrderDetails.Salesperson = @SalesPersonCode
4

3 回答 3

2

由于 fnc_M3_sql_datetime_to_M3_date 采用在整个查询执行过程中保持不变的值,因此将这两个调用(一个带有 startDate 和一个带有 endDate 的调用)移到查询的顶部,并将返回的值分配给声明的变量。然后引用那些声明的变量下面而不是在 where 子句中调用函数。这可能会有所帮助。函数有时会抑制良好查询计划的制定。

这个稍微讲一下 为什么SQL Server标量值函数变慢了? 这也是 http://strictlysql.blogspot.com/2010/06/scalar-functions-on-where-clause.html

declare @m3StartDate Numeric(8,0)
Set @m3StartDate = fnc_M3_sql_datetime_to_M3_date(@StartDate)
declare @m3EndDate Numeric(8,0)
Set @m3EndDate = fnc_M3_sql_datetime_to_M3_date(@EndDate)
...
WHERE AA_FactSalesOrderDetails.[Order Date] 
      BETWEEN @m3StartDate AND @m3EndDate
      AND
      AA_FactSalesOrderDetails.Salesperson = @SalesPersonCode

两个@m3-- vars 的类型应该与AA_FactSalesOrderDetails.[Order Date] 完全相同。

我还将检查 Dim_Customers 上获取扫描而不是查找的键的定义,并确保 Dim_Customers 的索引方式对您有所帮助(如果还没有的话)。 http://blog.sqlauthority.com/2009/08/24/sql-server-index-seek-vs-index-scan-diffefence-and-usage-a-simple-note/

于 2012-04-25T15:35:12.167 回答
0

我愿意赌这个版本的运行速度超过 35 秒。

现在,可能还有其他可能的优化(例如创建或改进索引,如果没有看到计划我们就无法知道),但我认为我已经清理了您查询中应该有助于提高性能的几个问题。

编辑一些编辑,因为显然用户正在针对 2000 运行,即使问题被标记为 2008 ......

-- make sure you don't have an implicit conversion between varchar and nvarchar
DECLARE
  @SalesPersonCode NVARCHAR(4),
  @StartDate DATETIME,
  @EndDate DATETIME;

SELECT
  @SalesPersonCode = N'AC', -- nvarchar needs N prefix!
-- get rid of the function call, I am guessing it just removes time
-- in which case, use the DATE data type instead.
  @StartDate       = '20120301',
  @EndDate         = '20120331';

-- since a salesperson can only have one code, and you are only pulling the name into the 
-- SELECT list (it will be the same for every row), use a constant and eliminate the join.

DECLARE @SalesPersonName NVARCHAR(255);

SELECT @SalesPersonName = SalesPerson_Name 
  FROM dbo.Dim_SalesOrganisation
  WHERE SalesPerson = @SalesPersonCode;

-- I've added table aliases which make the query MUCH, MUCH easier to read

SELECT f.Salesperson
     , Salesperson_name = @SalesPersonName
     , f.[Order Date]
     , c.[Customer number]
     , c.[Customer name]
     , c.[Area/state]
     , c.country
     , c.[Customer stop] AS [Customer Block]
     , f.[Customer order stop] AS [Co Stop]
     , f.[First delivery date Header]
     , f.[Last delivery date Header]
     , c.[User-defined field 6 - customer]
     , c.[Customer group name]
     , f.[Contact Method]
     , f.[Customer order number]
     , f.[Price Level]
     , f.[Item number]
     , i.[Product group description]  AS [Item name]
     , f.[Ordered quantity - basic U/M] AS [Quantity Ordered]
     , f.[Ordered quantity - basic U/M] * f.[Net price] AS [Order Line Total ]

    -- I've also added schema prefix. See below *
FROM 
     dbo.AA_FactSalesOrderDetails AS f
-- I've removed the join to Dim_SalesOrganisation as per above
     LEFT OUTER JOIN dbo.Dim_Customers AS c
       ON f.c_dKey = c.Dim_Customers_dKey
     LEFT OUTER JOIN dbo.Dim_Items AS i
       ON f.[Item number] = i.[Item number]
     -- I've removed the join to Dim_CustomerOrderTypes since it is never used
WHERE 
    -- in case [Order Date] is DATETIME and includes time information. See below **
    f.[Order Date] >= @StartDate 
    AND f.[Order Date] < DATEADD(DAY, 1, @EndDate)
    -- still need to restrict it to the stated salesperson
    AND f.SalesPerson = @SalesPersonCode;

* 要改掉的坏习惯:避免使用模式前缀

** 要改掉的坏习惯:错误处理日期/范围查询

于 2012-04-25T16:23:54.783 回答
0

尽管@hatchet 避免在WHERE子句中使用函数是正确的,但我想在这种情况下这不是问题,因为它是在标量值上使用的(只能通过实际的查询计划来确定)。

当然,您可以删除对 table 的引用Dim_CustomerOrderTypes,即不过滤也不返回任何数据。我相信这个查询应该使用以下索引来提高性能:

-- to seek on [Salesperson] and scan on [Order Date]
CREATE CLUSTERED INDEX IDXC ON AA_FactSalesOrderDetails([Salesperson], [Order Date]);

-- to seek on key
CREATE CLUSTERED INDEX IDXC ON Dim_Customers([Dim_Customers_dKey]);

-- to seek only this index instead of reading from table
CREATE INDEX IDX0 ON Dim_SalesOrganisation([Salesperson], [Salesperson name]);

-- to seek only this index instead of reading from table
CREATE INDEX IDX0 ON Dim_Items ([Item number], [Product group description])

我希望这些建议对你有所帮助。

于 2012-05-22T15:11:29.163 回答