需要减少查询时间

时间:2012-04-25 15:12:04

标签: sql-server sql-server-2008 tsql query-optimization

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

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

3 个答案:

答案 0 :(得分:2)

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

这谈到了一点 Why do SQL Server Scalar-valued functions get slower? 这也是 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/

答案 1 :(得分: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;

* Bad habits to kick : avoiding the schema prefix

** Bad habits to kick : mis-handling date / range queries

答案 2 :(得分:0)

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

当然,您可以删除对表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])

我希望这些建议可以帮助你。