2012-04-25 89 views
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 
+1

'dbo.fnc_M3_sql_datetime_to_M3_date()'做了什麼?如果代碼不是很討厭,你可以發佈它嗎? – Yuck 2012-04-25 15:14:13

+0

除了@Yuck的問題,表中有多少行?此查詢返回多少行?什麼是[Order Date]的數據類型和函數的返回數據類型?在[訂單日期],銷售人員或加入標準中的任何列上是否有索引?你看過執行計劃,看看時間在哪裏嗎? – 2012-04-25 15:16:37

+0

Dim_Customers.Dim_Customers_dKey是否有一個聚集索引覆蓋它?另外,'AA_FactSalesOrderDetails.Dim_Customers_dKey'是否包含一個覆蓋索引爲'Dim_Customers.Dim_Customers_dKey'的外鍵? – 2012-04-25 15:16:39

回答

2

由於fnc_M3_sql_datetime_to_M3_date取一個值,在整個查詢的執行恆定的,移動這兩個電話(具有的startDate和一個與結束日期到您的查詢的頂部,將返回的值聲明的變量,然後引用這些聲明的變量下方,而不是調用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/

+0

+1,但您必須使用分析器驗證函數調用實際上是否發生多次。在很多情況下(每個版本都會變得更好),SQL Server在優化這些調用方面非常出色 - 取決於函數的性質以及它們的使用位置。無論我還是同意他們應該被移出主查詢。 – 2012-04-25 15:43:46

+0

我不能肯定地說它會提高性能。有時候SQL Server可以很好地工作,我們不能看到函數中的內容。但考慮到海報提到的限制,這是一個簡單的嘗試,他的查詢非常簡單,所以我沒有看到很多其他選項。 – hatchet 2012-04-25 15:47:50

+0

如果我說的是無關緊要的方式,我很抱歉,但是如果我將這個查詢與UNION ALL關鍵字分開,那麼是否有可能減少時間?順便提一下,您可以告訴我如何才能做出您的建議? – ahmet 2012-04-25 15:51:04

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; 

*http://sqlblog.com/blogs/aaron_bertrand/archive/2009/10/11/bad-habits-to-kick-avoiding-the-schema-prefix.aspx

**http://sqlblog.com/blogs/aaron_bertrand/archive/2009/10/16/bad-habits-to-kick-mishandling-date-range-queries.aspx

+0

消息139,級別15,狀態1,行0 無法將默認值分配給本地變量。 消息137,級別15,狀態2,行16 必須聲明標量變量「@SalesPersonCode」。 消息137,級別15,狀態2,行52 必須聲明標量變量「@StartDate」。 – ahmet 2012-04-25 16:58:41

+0

SQL 2000?只需更改頂部的變量聲明部分,以便您不立即設置默認值,然後在聲明它們之後立即設置這些值。 – 2012-04-25 18:10:19

+0

您正在使用SQL Server 2000?爲什麼問題標記爲SQL Server 2008? – 2012-04-25 18:11:23

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]) 

我希望這些建議可以幫助你。