我的问题是下面的查询需要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
答案 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;
答案 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])
我希望这些建议可以帮助你。