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,均明显下降;

 

posted @ 2014-04-02 12:55  我是大菠萝  Views(903)  Comments(11Edit  收藏  举报