2016-03-18 3 views
4

Я написал ниже запрос для извлечения отдельного RegNo из двух разных таблиц. Но ниже запроса требуется около 25 секунд для получения результатов. В таблице инвентаря имеется более 1,5 миллиона записей.Как сократить время выполнения запроса на SQL-сервере?

Select F.PKID, F.RegNo 
From 
(
    Select E.PKID, E.RegNo 
    Row_Number() Over(Order By E.RegNo Asc) RowNo 
    From 
    (
    Select C.PKID, C.RegNo 
    From 
    (
     Select Pk_Id PKID, LTrim(RTrim(A.Reg_No)) RegNo, 
     Row_Number() Over(Partition By LTrim(RTrim(A.Reg_No)) 
     Order By (Select Null)) RegRowNo 
     From dbo.KeyreferenceDetails A (NoLock) 
     Where A.KeyreferenceStatus = 'L' 
     And A.Reg_No Like @Value And IsNull(Reg_No, '') <> '' And Not Exists 
     (
     Select 1 From dbo.INVENTORY B (NoLock) 
     Where A.Reg_No = B.Inv_H_Reg_No 
    ) 
    ) C 
    Where C.RegRowNo = 1 And IsNull(C.RegNo, '') <> '-' 
    Union 
    Select D.PKID, D.RegNo 
    From 
    (
     Select Pk_ID PKID, LTrim(LTrim(Txt_RegNo)) RegNo, 
     Row_Number() Over(Partition By LTrim(LTrim(A.Txt_RegNo)) 
     Order By (Select Null)) RegRowNo 
     From dbo.MobileMessageDetails A (Nolock) 
     Left Join dbo.PLACE P (Nolock) On P.Place_Shrt_Code = A.Txt_YarddCode 
     And P.[Status] = 'L' 
     Left Join dbo.INVENTORY B (Nolock) On A.Txt_RegNo = B.Inv_H_Reg_No    
     Where A.Txt_INOUT In('IN', 'MOBILE') And IsNull(A.Txt_RegNo, '') <> '' And B.Inv_H_Pk_Id Is Null 
     And A.[Status] = 'L' And Txt_RegNo Like @Value 
    ) D 
    Where D.RegRowNo = 1 And IsNull(D.RegNo, '') <> '-' 
) E 
) F 
Where F.RowNo > 0 And F.RowNo <= 20 

План запроса:

enter image description here

Доступные индексы:

KeyreferenceDetails table:

Index Name ---------------+ Column Name ----------------- + Index Type 
IX_KeyreferenceDetails_I | Reg_No      | NONCLUSTERED 
IX_KeyreferenceDetails_II | KeyreferenceStatus   | NONCLUSTERED 

Inventory table:

Index Name ---------------+ Column Name ----------------- + Index Type 
IX_Inventory_I   | Inv_H_Reg_No     | NONCLUSTERED 

MobileMessageDetails table:

Index Name --------------- + Column Name ----------------- + Index Type 
IX_MobileMessageDetails_I | Txt_RegNo      | NONCLUSTERED 
IX_MobileMessageDetails_II | Txt_INOUT      | NONCLUSTERED 

Place table:

Index Name ---------------+ Column Name ----------------- + Index Type 
IX_Place_I    | Place_Shrt_Code    | NONCLUSTERED 
IX_Place_I    | Status      | NONCLUSTERED 

Я создал требуемые индексы для всех используемых таблиц в приведенном выше запросе. Но стоимость запросов высока. Как сократить время выполнения запросов на SQL-сервере?

Statistics Output:

SQL Server Execution Times: 
CPU time = 0 ms, elapsed time = 0 ms. 
Table 'INVENTORY'. Scan count 6, logical reads 382, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 
Table 'KeyreferenceDetails'. Scan count 15, logical reads 9062, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 
Table 'Mobile_MessageDetails'. Scan count 1, logical reads 4, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 
Table '#TempItemsCount_____________________________________________________________________________________________________0000000118A9'. Scan count 0, logical reads 1, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 

SQL Server Execution Times: 
CPU time = 20733 ms, elapsed time = 7844 ms. 
Table 'INVENTORY'. Scan count 6, logical reads 382, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 
Table 'KeyreferenceDetails'. Scan count 14, logical reads 9062, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 
Table 'Mobile_MessageDetails'. Scan count 1, logical reads 4, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 
Table '#VehicleRegDetails__________________________________________________________________________________________________0000000118AB'. Scan count 0, logical reads 20, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 

SQL Server Execution Times: 
CPU time = 21139 ms, elapsed time = 8146 ms. 
Table '#TABLE_SCHEMA_______________________________________________________________________________________________________0000000118AA'. Scan count 0, logical reads 1, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0. 

Update:

Insert Into #TempItemsCount(TotalCount) 
Select Count(E.PKID) 
From 
(
    Select E.PKID, E.RegNo 
     Row_Number() Over(Order By E.RegNo Asc) RowNo 
     From 
     (
     Select C.PKID, C.RegNo 
     From 
     (
      Select Pk_Id PKID, LTrim(RTrim(A.Reg_No)) RegNo, 
      Row_Number() Over(Partition By LTrim(RTrim(A.Reg_No)) 
      Order By (Select Null)) RegRowNo 
      From dbo.KeyreferenceDetails A (NoLock) 
      Where A.KeyreferenceStatus = 'L' 
      And A.Reg_No Like @Value And IsNull(Reg_No, '') <> '' And Not Exists 
      (
      Select 1 From dbo.INVENTORY B (NoLock) 
      Where A.Reg_No = B.Inv_H_Reg_No 
     ) 
     ) C 
     Where C.RegRowNo = 1 And IsNull(C.RegNo, '') <> '-' 
     Union 
     Select D.PKID, D.RegNo 
     From 
     (
      Select Pk_ID PKID, LTrim(LTrim(Txt_RegNo)) RegNo, 
      Row_Number() Over(Partition By LTrim(LTrim(A.Txt_RegNo)) 
      Order By (Select Null)) RegRowNo 
      From dbo.MobileMessageDetails A (Nolock) 
      Left Join dbo.PLACE P (Nolock) On P.Place_Shrt_Code = A.Txt_YarddCode 
      And P.[Status] = 'L' 
      Left Join dbo.INVENTORY B (Nolock) On A.Txt_RegNo = B.Inv_H_Reg_No    
      Where A.Txt_INOUT In('IN', 'MOBILE') And IsNull(A.Txt_RegNo, '') <> '' And B.Inv_H_Pk_Id Is Null 
      And A.[Status] = 'L' And Txt_RegNo Like @Value 
     ) D 
     Where D.RegRowNo = 1 And IsNull(D.RegNo, '') <> '-' 
    ) E 
) 
+0

каковы результаты sys.dm_db_missing_index_details? –

+0

Измените CTE на временные таблицы. (обратите внимание, а не на переменные таблицы) –

+0

У вас, похоже, есть индексы для полей, заданных в качестве критериев поиска, таких как 'status' и' TXT_inout', но они не используются. Действительно ли это не ограничивает объем данных? Также может оказаться полезной и статистика io. –

ответ

2

Answering без схемы и с ограниченной информацией, так что никаких гарантий это разбором, но ниже является попыткой оптимизировать его так, что вы может по крайней мере понять подходы.

оптимизаций можно разделить на несколько пунктов:

  1. Разделение сложного запроса на отдельные заявления, которые легче понять (как для людей & оптимизатор) и известно, что оптимизатор будет хорошо с. Например, легко оптимизировать первый запрос, потому что ясно, что Reg_No используется в JOIN, а также в предложении WHERE. Пример индекс может быть:

    CREATE INDEX Некластеризованного имя_индекс ON dbo.KeyreferenceDetails (Reg_No) включает (Pk_Id, KeyreferenceStatus) где KeyreferenceStatus = 'L'

  2. Устранить функции (ISNULL, COALESCE, LTRIM, RTRIM и т.д.) в JOIN, WHERE, PARTITION BY.Например, рассмотрим следующее:

    WHERE ISNULL(A.Reg_No, '') <> ''

    оптимизатор не сможет использовать индекс Reg_No, потому что вы подаете функцию к нему. Вместо этого, перепишем это так:

    WHERE A.Reg_No <> '' AND A.Reg_No IS NOT NULL

  3. Рассмотрим использование UNION ALL против UNION. Для UNION механизм запроса дедуплицирует два набора и возвращает только уникальные данные. Он должен сделать это, прежде чем он вернет любые данные для обработки. С UNION ALL у вас есть конкатенация, которая просто обрабатывает два запроса независимо и добавляет второй набор в конец первого.

  4. Вместо того чтобы использовать в IN в пункте WHERE, вы можете использовать LEFT OUTER JOIN и добавить проверку, чтобы убедиться, что ключевой столбец из присоединяемой таблицы NULL, чтобы гарантировать, что никакие записи не возвращаются из него, или EXISTS, и это часто будет выполняться более эффективно.

Ниже приведен пример одного подхода к применению некоторых из этих принципов к вашему запросу:

IF OBJECT_ID('tempdb..#temp') IS NOT NULL DROP TABLE #temp; 
GO 
SELECT PKID = Pk_Id, 
     RegNo = LTrim(RTrim(A.Reg_No)), 
     RegRowNo = Row_Number() Over(Partition By LTrim(RTrim(A.Reg_No)) Order By (Select Null)) 
INTO #temp 
FROM dbo.KeyreferenceDetails A WITH(NOLOCK) 
     LEFT OUTER JOIN dbo.INVENTORY B WITH(NOLOCK) ON A.Reg_No = B.Inv_H_Reg_No 
WHERE B.Inv_H_Reg_No IS NULL 
AND  A.KeyreferenceStatus = 'L' 
And  A.Reg_No Like @Value 
And  A.Reg_No IS NOT NULL 
AND  A.Reg_No <> ''; 

INSERT INTO #temp (PKID, RegNo, RegRowNo) 
    SELECT PKID = Pk_ID, 
      RegNo = LTrim(LTrim(A.Txt_RegNo)), 
      RegRowNo = Row_Number() Over(Partition By LTrim(LTrim(A.Txt_RegNo)) Order By (Select Null)) 
    FROM dbo.MobileMessageDetails A WITH(NOLOCK) 
      LEFT OUTER JOIN dbo.PLACE P WITH(NOLOCK) ON P.Place_Shrt_Code = A.Txt_YarddCode AND P.[Status] = 'L' 
      LEFT OUTER JOIN dbo.INVENTORY B WITH(NOLOCK) ON A.Txt_RegNo = B.Inv_H_Reg_No    
    WHERE B.Inv_H_Pk_Id Is Null 
    AND  A.Status = 'L' 
    AND  A.Txt_RegNo Like @Value 
    And  A.Txt_RegNo IS NOT NULL 
    AND  A.Txt_RegNo <> '' 
    AND  A.Txt_INOUT In ('IN', 'MOBILE'); 

IF OBJECT_ID('tempdb..#final') IS NOT NULL DROP TABLE #final; 
GO 
SELECT t.PKID, 
     t.RegNo, 
     RowNo = Row_Number() Over(Order By t.RegNo Asc) 
INTO #final 
FROM #temp t; 
WHERE t.RegNo <> '-' 

SELECT F.PKID, F.RegNo 
FROM #final F 
WHERE F.RowNo BETWEEN 1 AND 20 

GO 
DROP TABLE #temp, #final 

Поскольку вполне возможно, синтаксические ошибки в этом, при просмотре этого, я рекомендовал бы беговой каждый раздел за раз и обеспечение его работы, а не выполнение всего этого сценария сразу. Надеюсь это поможет!

С уважением,

Росс

+0

Но выше запрос занимает почти 14 секунд, чтобы вернуть результат. Запрос в моем вопросе занимает 16 секунд, чтобы вернуть результат после изменения некоторых условий. Как сократить время выполнения запроса? – RGS

+0

Как долго каждый из операторов выбора принимает в моем примере? Попытка понять, где узкое место. –

+0

Нужно ли включать Pk_Id в Non Clustered Index, потому что Pk_Id является первичным ключом, то есть кластеризованным индексом в моей таблице? – RGS