2012-04-25 3 views
0

Моя проблема ниже запроса занимает 38 секунд, Мне нужно уменьшить это время сколько угодно. Когда я смотрю план выполнения:% 54 тратит расходы на сканирование индексов Dim_Customers. Любое предложение будет оценено по достоинству. БлагодаряНеобходимо уменьшить время запроса

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()' do? Если код нелогично длинный, вы можете его опубликовать? – Yuck

+0

В дополнение к вопросу @ Yuck, сколько строк в таблицах? Сколько строк возвращается этим запросом? Каков тип данных [Дата заказа] и тип данных возврата функции? Есть ли указатель на [Дата заказа], Продавец или любой из столбцов в критерии соединения? Вы посмотрели план выполнения, чтобы узнать, где тратится время? –

+0

Есть ли у 'Dim_Customers.Dim_Customers_dKey' кластеризованный индекс?Кроме того, 'AA_FactSalesOrderDetails.Dim_Customers_dKey' является внешним ключом для' Dim_Customers.Dim_Customers_dKey' с индексом покрытия? –

ответ

2

Поскольку fnc_M3_sql_datetime_to_M3_date принимает значение, которое является постоянным на протяжении выполнения запроса, переместить эти два вызова (один с STARTDATE и один с ENDDATE в верхней части запроса и назначить возвращаемые значения объявленные переменные. Тогда ссылки эти объявленные переменные ниже вместо вызова функции внутри где п. Это может помочь. функции иногда подавляют выработку хорошего плана запроса.

Это говорит немного об этом 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. [Дата заказа].

Я также рассмотрю определение ключа на 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 довольно хорошо оптимизирует эти вызовы - зависит от характера функции и того, где они используются. Несмотря на это, я бы согласился с тем, что они должны быть удалены из основного запроса. –

+0

Я не могу сказать окончательно, что это улучшит производительность. Иногда SQL Server отлично работает с функциями, и мы не можем видеть, что находится в этой функции. Но, учитывая ограничения, о которых говорил плакат, это простая задача, и его запрос довольно прост, поэтому я не видел много других вариантов. – hatchet

+0

Прошу прощения, если я говорю неважно, но можно ли сократить время, если я делю этот запрос на ключевое слово UNION ALL? Кстати, вы могли бы показать мне, как я могу сделать ваше предложение? – ahmet

0

Я готов поспорить, что эта версия работает быстрее 35 секунд.

Теперь возможны другие оптимизации (например, создание или улучшение индексов, которые мы не можем знать, не видя плана), но я думаю, что я очистил несколько вопросов в вашем запросе, которые должны помочь повысить производительность ,

EDIT несколько правок с момента, по-видимому пользователь работает с 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

Msg 139, Level 15, State 1, Line 0 Невозможно присвоить значение по умолчанию локальной переменной. Msg 137, уровень 15, состояние 2, строка 16 Должен объявить скалярную переменную «@SalesPersonCode». Msg 137, Level 15, State 2, Line 52 Должен объявить скалярную переменную «@StartDate». – ahmet

+0

SQL 2000? Просто измените раздел объявления переменных сверху, чтобы сразу не устанавливать значение по умолчанию, а затем установите эти значения сразу после их объявления. –

+0

Вы используете SQL Server 2000? Почему проблема связана с SQL Server 2008? –

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

Я надеюсь, что эти предложения помогут вам.