SQL優化(1)


 

 

背景:StoreNotifyMainTask為主表,StoreNotifySubTask為子表,應用幾秒鍾關聯查詢一下,根據主、子表的條件查出top 100;

         目前主表記錄數648W,單表符合條件的記錄647W(基本全部符合條件)

         子表記錄數425W,單表符合條件的記錄106W

         主表id列與子表maintaskid為邏輯主外鍵關系

 

         由於子表條件固定,於是創建篩選索引

1 CREATE NONCLUSTERED INDEX [idxw_StoreNotifySubTask_RetryNum_yn_MainTaskId_inc] ON [dbo].[StoreNotifySubTask]
2 (
3     [RetryNum] ASC,
4     [YN] ASC
5 )
6 INCLUDE ( [MainTaskId])
7 WHERE ([RetryNum]<(3) AND [NotifyState]=(0) AND [yn]=(1))
View Code

初始的SQL如下:

 1 SELECT TOP 100
 2         sub.Id ,
 3         sub.SubscriberId ,
 4         sub.MainTaskId ,
 5         sub.Pin ,
 6         sub.BlogPin ,
 7         sub.SkuId ,
 8         sub.SkuName ,
 9         sub.Wpid1 ,
10         sub.Wpid2 ,
11         sub.Wpid3 ,
12         sub.Email ,
13         sub.PhoneNo ,
14         sub.Price ,
15         sub.SendPrice ,
16         sub.RetryNum ,
17         sub.AddressId ,
18         sub.CreateTime ,
19         ISNULL(sub.MessageTag, 0) AS MessageTag ,
20         sub.UpdateTime ,
21         sub.SendTime ,
22         sub.NotifyState ,
23         sub.YN ,
24         sub.Ext ,
25         sub.SkuPicUrl ,
26         sub.SubscriberTime
27 FROM    StoreNotifySubTask sub WITH ( NOLOCK) 
28         INNER JOIN StoreNotifyMainTask main ( NOLOCK ) ON sub.MainTaskId = main.Id
29 WHERE   main.TaskState = 2
30         AND main.YN = 1
31         AND sub.NotifyState = 0
32         AND sub.RetryNum < 3
33         AND sub.YN = 1
View Code

         執行計划:子表無法使用篩選索引

 

        

(0 行受影響)

表'StoreNotifyMainTask'。掃描計數0,邏輯讀取4333270 次,物理讀取0 次,預讀0 次,lob 邏輯讀取0 次,lob 物理讀取0 次,lob 預讀0 次。

表'StoreNotifySubTask'。掃描計數1,邏輯讀取209314 次,物理讀取0 次,預讀0 次,lob 邏輯讀取0 次,lob 物理讀取0 次,lob 預讀0 次。

 

SQL Server 執行時間:

   CPU 時間= 10592 毫秒,占用時間= 11298 毫秒。

 

強制使用篩選索引,導致子表邏輯讀上升;

 

(0 行受影響)

表'StoreNotifyMainTask'。掃描計數0,邏輯讀取4333270 次,物理讀取0 次,預讀0 次,lob 邏輯讀取0 次,lob 物理讀取0 次,lob 預讀0 次。

表'StoreNotifySubTask'。掃描計數1,邏輯讀取4338240 次,物理讀取0 次,預讀0 次,lob 邏輯讀取0 次,lob 物理讀取0 次,lob 預讀0 次。

 

SQL Server 執行時間:

   CPU 時間= 11965 毫秒,占用時間= 12014 毫秒。

 

優化思路:盡管子表中單表符合條件的記錄有106W,但按照maintaskid分組查詢發現,一個maintaskid有大量的子記錄,實際符合條件的maintaskid只有130個左右

於是,先從子表中查詢符合條件的的maintaskid(做group by),然后驗證這些maintaskid在主表中是否符合條件,再回到子表中按照最終篩選的maintaskid做自關聯;

由於業務邏輯上可能出現子表中同一個maintaskid也包含不符合條件的記錄,因此最后一步的按照maintaskid的子表自關聯,還需要加上之前子表的條件作為限定;

 

 

優化后的SQL:

; WITH cte AS (    
   SELECT sub.MainTaskId
   FROM    StoreNotifySubTask sub WITH ( NOLOCK )
WHERE sub.NotifyState = 0
        AND sub.RetryNum < 3
        AND sub.YN = 1
GROUP BY MainTaskId
)
,cte1 AS(
SELECT  MainTaskId FROM cte where
EXISTS(SELECT id FROM StoreNotifyMainTask main ( NOLOCK ) WHERE  cte.MainTaskId = main.Id AND  main.TaskState = 2
        AND main.YN = 1)
)
select TOP 100
        sub.Id ,
        sub.SubscriberId ,
        sub.MainTaskId ,
        sub.Pin ,
        sub.BlogPin ,
        sub.SkuId ,
        sub.SkuName ,
        sub.Wpid1 ,
        sub.Wpid2 ,
        sub.Wpid3 ,
        sub.Email ,
        sub.PhoneNo ,
        sub.Price ,
        sub.SendPrice ,
        sub.RetryNum ,
        sub.AddressId ,
        sub.CreateTime ,
        ISNULL(sub.MessageTag, 0) AS MessageTag ,
        sub.UpdateTime ,
        sub.SendTime ,
        sub.NotifyState ,
        sub.YN ,
        sub.Ext ,
        sub.SkuPicUrl ,
        sub.SubscriberTime
FROM    StoreNotifySubTask sub WITH ( NOLOCK ) JOIN cte1 ON sub.MainTaskId=cte1.maintaskid
WHERE sub.NotifyState = 0
        AND sub.RetryNum < 3
        AND sub.YN = 1
View Code

 

上述SQL中,可以將cte和cte1合並,改為:

 1 ; WITH cte AS (    
 2    SELECT sub.MainTaskId
 3    FROM    StoreNotifySubTask sub WITH ( NOLOCK )
 4 WHERE sub.NotifyState = 0
 5         AND sub.RetryNum < 3
 6         AND sub.YN = 1
 7         AND EXISTS(SELECT id FROM StoreNotifyMainTask main ( NOLOCK ) WHERE  sub.MainTaskId = main.Id AND  main.TaskState = 2
 8         AND main.YN = 1)
 9 GROUP BY MainTaskId
10 )
View Code

 

對比執行結果:

優化前:

(0 行受影響)

表'StoreNotifyMainTask'。掃描計數0,邏輯讀取4333270 次,物理讀取0 次,預讀0 次,lob 邏輯讀取0 次,lob 物理讀取0 次,lob 預讀0 次。

表'StoreNotifySubTask'。掃描計數1,邏輯讀取209361 次,物理讀取0 次,預讀0 次,lob 邏輯讀取0 次,lob 物理讀取0 次,lob 預讀0 次。

 

SQL Server 執行時間:

   CPU 時間= 12465 毫秒,占用時間= 12599 毫秒。

 

 

優化后:

(0 行受影響)

表'Worktable'。掃描計數0,邏輯讀取0 次,物理讀取0 次,預讀0 次,lob 邏輯讀取0 次,lob 物理讀取0 次,lob 預讀0 次。

表'StoreNotifySubTask'。掃描計數1,邏輯讀取4970 次,物理讀取0 次,預讀0 次,lob 邏輯讀取0 次,lob 物理讀取0 次,lob 預讀0 次。

表'StoreNotifyMainTask'。掃描計數0,邏輯讀取537 次,物理讀取0 次,預讀0 次,lob 邏輯讀取0 次,lob 物理讀取0 次,lob 預讀0 次。

表'Worktable'。掃描計數0,邏輯讀取0 次,物理讀取0 次,預讀0 次,lob 邏輯讀取0 次,lob 物理讀取0 次,lob 預讀0 次。

 

SQL Server 執行時間:

   CPU 時間= 312 毫秒,占用時間= 323 毫秒。

 

 

執行時間、IO,均明顯下降;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM