今天分析OR的优化方式,主要是案例进行剖析,单纯的说这个优化有多明显我认为是完全没有意义的,任何东西,任何道理,没有数据说话,总是会让人质疑罢了

案例语句

某大型房地产公司,巡检日期2013-04-23,问题语句

View Code
 1 SELECT  SUM(num) AS Num ,
 2         HZLevel
 3 FROM    ( SELECT    ISNULL(SUM(1), 0) AS num ,
 4                     '三年内合作' AS HZLevel
 5           FROM      ( SELECT DISTINCT
 6                                 Providerguid
 7                       FROM      vp_Provider2UnitGrid a
 8                                 LEFT JOIN cb_Contract b ON ( b.YfProviderGUID = a.Providerguid
 9                                                              OR b.bfProviderGUID = a.Providerguid
10                                                            )
11                       WHERE     DATEDIFF(year, b.SignDate, GETDATE()) <= 3
12                     ) a
13           UNION ALL
14           SELECT    ISNULL(SUM(1), 0) AS num ,
15                     '三年前合作' AS HZLevel
16           FROM      ( SELECT    Providerguid ,
17                                 MAX(SignDate) AS m
18                       FROM      vp_Provider2UnitGrid a
19                                 LEFT JOIN cb_contract b ON ( a.Providerguid = b.YfProviderGUID
20                                                              OR a.Providerguid = b.bfProviderGUID
21                                                            )
22                       GROUP BY  Providerguid
23                     ) a
24           WHERE     DATEDIFF(year, m, GETDATE()) > 3
25           UNION ALL
26           SELECT    ISNULL(SUM(1), 0) AS num ,
27                     '无合作' AS HZLevel
28           FROM      ( SELECT DISTINCT
29                                 Providerguid
30                       FROM      vp_Provider2UnitGrid
31                       WHERE     Providerguid NOT IN (
32                                 SELECT DISTINCT
33                                         YfProviderGUID
34                                 FROM    cb_Contract
35                                 WHERE   YfProviderGUID IS NOT NULL
36                                 UNION
37                                 SELECT DISTINCT
38                                         bfProviderGUID
39                                 FROM    cb_Contract
40                                 WHERE   bfProviderGUID IS NOT NULL )
41                     ) a
42         ) a
43 GROUP BY HZLevel
44 UNION ALL
45 SELECT  ISNULL(SUM(1), 0) AS num ,
46         '合计' AS HZLevel
47 FROM    ( SELECT DISTINCT
48                     providerguid ,
49                     hzlevel
50           FROM      vp_Provider2UnitGrid
51         ) a
52 ORDER BY HZLevel DESC
View Code
SQL Server 分析和编译时间: 
   CPU 时间 = 187 毫秒,占用时间 = 247 毫秒。

(4 行受影响)
表 'Worktable'。扫描计数 8,逻辑读取 823264 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'cb_Contract'。扫描计数 18,逻辑读取 15552 次,物理读取 2 次,预读 1738 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'p_Provider'。扫描计数 16,逻辑读取 548 次,物理读取 2 次,预读 111 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'Worktable'。扫描计数 0,逻辑读取 0 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

SQL Server 执行时间:
   CPU 时间 = 29717 毫秒,占用时间 = 8419 毫秒。
警告: 聚合或其他 SET 操作消除了空值。
SQL Server 分析和编译时间: 
   CPU 时间 = 0 毫秒,占用时间 = 0 毫秒。

SQL Server 执行时间:
   CPU 时间 = 0 毫秒,占用时间 = 0 毫秒。

 

执行时间为8.4S,IO也是非常高的,类似这种语句如何下手?其实方法很简单,优化的效果也是非常好的

分析手段

因为语句中有UNION ALL,所以不用开执行计划,直接把每个UNION ALL起来的语句分别执行,找出最慢的语句即可

 1 SELECT    ISNULL(SUM(1), 0) AS num ,
 2                     '三年内合作' AS HZLevel
 3           FROM      ( SELECT DISTINCT
 4                                 Providerguid
 5                       FROM      vp_Provider2UnitGrid a
 6                                 LEFT JOIN cb_Contract b ON ( b.YfProviderGUID = a.Providerguid
 7                                                              OR b.bfProviderGUID = a.Providerguid
 8                                                            )
 9                       WHERE     DATEDIFF(year, b.SignDate, GETDATE()) <= 3
10                     ) a
 1     SELECT    ISNULL(SUM(1), 0) AS num ,
 2                     '三年前合作' AS HZLevel
 3           FROM      ( SELECT    Providerguid ,
 4                                 MAX(SignDate) AS m
 5                       FROM      vp_Provider2UnitGrid a
 6                                 LEFT JOIN cb_contract b ON ( a.Providerguid = b.YfProviderGUID
 7                                                              OR a.Providerguid = b.bfProviderGUID
 8                                                            )
 9                       GROUP BY  Providerguid
10                     ) a
11           WHERE     DATEDIFF(year, m, GETDATE()) > 3
通过分段去执行查找发现,上面的2个语句非常慢,那么还是从局部入手改变语序,以达到优化整段SQL的目的,如何确定问题点在哪?这其实非常关键,也是重点要讲的
1,where条件不符合SARG,这样导致无法用到索引
2,使用了视图,导致取数不是最优的,看是不是能按基表取数
3,OR的影响,OR和NOT IN一样,也会导致索引用不上,其实最可恶的是,OR还可能导致执行计划不是最优
那么影响点在哪里?  请看下面
 1 SELECT    ISNULL(SUM(1), 0) AS num ,
 2                     '无合作' AS HZLevel
 3           FROM      ( SELECT DISTINCT
 4                                 Providerguid
 5                       FROM      vp_Provider2UnitGrid
 6                       WHERE     Providerguid NOT IN (
 7                                 SELECT DISTINCT
 8                                         YfProviderGUID
 9                                 FROM    cb_Contract
10                                 WHERE   YfProviderGUID IS NOT NULL
11                                 UNION
12                                 SELECT DISTINCT
13                                         bfProviderGUID
14                                 FROM    cb_Contract
15                                 WHERE   bfProviderGUID IS NOT NULL )
16                     ) a

刚刚列出了有问题的SQL,没有这段,说明这段SQL是没问题的,确实也是如此,这段SQL也用到了同样的视图, where条件也不符合SARG,但是它的速度确非常快

那么我有理由最先怀疑,问题点就出在OR这里,那么我把OR这个条件注释掉,再来看整个SQL语句,发现速度变快了,那么只需要优化OR即可


如何优化

 1 SELECT    ISNULL(SUM(1), 0) AS num ,
 2                     '三年内合作' AS HZLevel
 3           FROM      ( SELECT DISTINCT
 4                                 Providerguid
 5                       FROM      vp_Provider2UnitGrid a
 6                                 LEFT JOIN cb_Contract b ON ( b.YfProviderGUID = a.Providerguid
 7                                                              --OR b.bfProviderGUID = a.Providerguid
 8                                                            )
 9                       WHERE     DATEDIFF(year, b.SignDate, GETDATE()) <= 3
10                       UNION 
11                       SELECT DISTINCT
12                                 Providerguid
13                       FROM      vp_Provider2UnitGrid a
14                                 LEFT JOIN cb_Contract b ON (  b.bfProviderGUID = a.Providerguid
15                                                            )
16                       WHERE     DATEDIFF(year, b.SignDate, GETDATE()) <= 3
17                       
18                     ) a
19                    
20           UNION ALL
21           SELECT    ISNULL(SUM(1), 0) AS num ,
22                     '三年前合作' AS HZLevel
23           FROM      ( SELECT    Providerguid ,
24                                 MAX(SignDate) AS m
25                       FROM      vp_Provider2UnitGrid a
26                                 LEFT JOIN cb_contract b ON ( a.Providerguid = b.YfProviderGUID
27                                                              --OR a.Providerguid = b.bfProviderGUID
28                                                            )
29                       GROUP BY  Providerguid
30                       UNION 
31                        SELECT    Providerguid ,
32                                 MAX(SignDate) AS m
33                       FROM      vp_Provider2UnitGrid a
34                                 LEFT JOIN cb_contract b ON (  a.Providerguid = b.bfProviderGUID
35                                                            )
36                       GROUP BY  Providerguid
37                     ) a
(2 行受影响)
表 'Worktable'。扫描计数 0,逻辑读取 0 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'p_Provider'。扫描计数 4,逻辑读取 224 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'cb_Contract'。扫描计数 4,逻辑读取 6088 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

(1 行受影响)

SQL Server 执行时间:
   CPU 时间 = 63 毫秒,占用时间 = 85 毫秒。
SQL Server 分析和编译时间: 
   CPU 时间 = 0 毫秒,占用时间 = 0 毫秒。

SQL Server 执行时间:
   CPU 时间 = 0 毫秒,占用时间 = 0 毫秒。

 

只需要把OR改成UNION即可,简单看一下,优化前后的对比结果,可谓天壤之别

招聘信息

1. 工作地点:武汉 

2. 工作经验2年以上

3. asp.net开发, 基础夯实。

公司名称:武汉明源动力软件有限公司  

给我发邮件:236773862@qq.com 

 

posted on 2013-04-23 18:06  zhourui  阅读(3990)  评论(7编辑  收藏  举报