一直从事运维的工作,免不了优化一些SQL语句,因为本人比较懒的原因,很多经典的案例没有记录下来,深表遗憾

案例语句

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

 1  SELECT COUNT(*)
 2  FROM   ( SELECT    temp.Application ,
 3                     COUNT(*) AS UserCount
 4           FROM      ( SELECT    f.Application
 5                       FROM      myUserRights ur
 6                                 INNER JOIN myFunction f ON ur.ObjectType = f.FunctionCode
 7                                 INNER JOIN myUser u ON ur.UserGUID = u.UserGUID
 8                       WHERE     u.IsAdmin = 0
 9                                 AND ( IsDisabeld = 0
10                                       OR IsDisabeld IS NULL
11                                     )
12                                 AND ur.UserGUID NOT IN (
13                                 SELECT  UserGUID
14                                 FROM    myUserRoles
15                                 WHERE   RoleGUID = 'dbf6cef3-5dc1-4b21-9865-4664849eac78' )
16                       GROUP BY  f.Application ,
17                                 ur.UserGUID
18                     ) AS temp
19           GROUP BY  temp.Application
20         ) AS temp2
21         RIGHT   JOIN myApplication a ON a.Application = temp2.Application
22  WHERE  a.Level = 1
23         AND a.Application IN (
24         SELECT  Application
25         FROM    myFunction
26         WHERE   FunctionCode IN ( '', '01010102', '01010104', '01010105',
27                                   '01010111', '01010106', '01010107',
28                                   '01010110', '01010118', '01010604',
29                                   '01010603', '01010202', '01010203',
30                                   '01010205', '01010206', '01010207',
31                                   '01010301', '01010302', '01010303',
32                                   '01010304', '01010308', '01010305',
33                                   '01010306', '01010310', '01010309',
34                                   '01010407', '01010401', '01010403',
35                                   '01010404', '01010406', '01010408',
36                                   '01010409', '01010410', '01010411',
37                                   '01010506', '01010502', '01010503',
38                                   '01010507', '01010509', '01011999',
39                                   '06636E7E_F243_4CAE_86AB_723CFBA1513C',
40                                   '06636E7E_F243_4CAE_86AB_723CFBA1513C',
41                                   '0741CB62_C25A_4B57_B0A9_2236BA8BAE97',
42                                   '0741CB62_C25A_4B57_B0A9_2236BA8BAE97',
43                                   '458E20D3_51B9_4A87_96B9_7005CAD462F0',
44                                   '458E20D3_51B9_4A87_96B9_7005CAD462F0',
45                                   '4B3F9D72_3427_4ECA_B089_78E2B9A76375',
46                                   '4B3F9D72_3427_4ECA_B089_78E2B9A76375',
47                                   '945F14D1_717B_4434_89AF_E4DF11E03617',
48                                   '945F14D1_717B_4434_89AF_E4DF11E03617',
49                                   'C035C1A7_7612_421C_800B_690FDB4E4E44',
50                                   'C035C1A7_7612_421C_800B_690FDB4E4E44',
51                                   'C6E3E43E_19EF_43E6_88B8_6C9DFC4363A1',
52                                   'C6E3E43E_19EF_43E6_88B8_6C9DFC4363A1',
53                                   'F7BFE91E_D79B_48BD_8233_E92A63A01921',
54                                   'F7BFE91E_D79B_48BD_8233_E92A63A01921' )
55         GROUP BY Application )
56         AND ( CASE WHEN temp2.UserCount IS NULL THEN 0 + 17
57                    ELSE temp2.UserCount + 0 + 17
58               END ) > a.LicenseUserCount
表 'Worktable'。扫描计数 1,逻辑读取 109 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'myUserRoles'。扫描计数 1,逻辑读取 73 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'myUserRights'。扫描计数 2772576,逻辑读取 11812753 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'myFunction'。扫描计数 2,逻辑读取 90043 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'myUser'。扫描计数 1,逻辑读取 163 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'myApplication'。扫描计数 1,逻辑读取 2 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

(1 行受影响)
表 'Worktable'。扫描计数 1,逻辑读取 109 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'myUserRoles'。扫描计数 1,逻辑读取 73 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'myUserRights'。扫描计数 2772576,逻辑读取 11812753 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'myFunction'。扫描计数 2,逻辑读取 90043 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'myUser'。扫描计数 1,逻辑读取 163 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'myApplication'。扫描计数 1,逻辑读取 2 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

(1 行受影响)

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

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

IO上千万,该语句是对应的一个展示页面,统计count(*)是为了分页,执行时间为12S,开始拿到该语句,一头雾水,不知如何下手。

分析手段

说说我的分析手段,这里无法把数据库给大家,所以只能是分享我的分析手段,能给大家启发就达到了本文的目的,一般拿到这种SQL语句,我首先执行一遍

然后看执行计划,找最大的开销,看能不能加索引优化,看多了也没太大用,

最大的开销在索引查找上,其实我最讨厌的就是这种,一看最大开销是索引查找,也没办法加索引去优化,一般遇到这种情况,是因为选错了执行计划导致的,

选错了执行计划是代表了你写的SQL有问题,有什么问题?SQL语句中有OR,NOT IN这些不符合SARG的,我采取了一个比较有效的验证方式,强制改变连接

方式,很暴力,也很危险

 1  SELECT COUNT(*)
 2  FROM   ( SELECT    temp.Application ,
 3                     COUNT(*) AS UserCount
 4           FROM      ( SELECT    f.Application
 5                       FROM      myUserRights ur
 6                                 INNER JOIN myFunction f ON ur.ObjectType = f.FunctionCode
 7                                 INNER JOIN myUser u ON ur.UserGUID = u.UserGUID
 8                       WHERE     u.IsAdmin = 0
 9                                 AND ( IsDisabeld = 0
10                                       OR IsDisabeld IS NULL
11                                     )
12                                 AND ur.UserGUID NOT IN (
13                                 SELECT  UserGUID
14                                 FROM    myUserRoles
15                                 WHERE   RoleGUID = 'dbf6cef3-5dc1-4b21-9865-4664849eac78' )
16                       GROUP BY  f.Application ,
17                                 ur.UserGUID
18                     ) AS temp
19           GROUP BY  temp.Application
20         ) AS temp2
21         RIGHT HASH   JOIN myApplication a ON a.Application = temp2.Application
22  WHERE  a.Level = 1
23         AND a.Application IN (
24         SELECT  Application
25         FROM    myFunction
26         WHERE   FunctionCode IN ( '', '01010102', '01010104', '01010105',
27                                   '01010111', '01010106', '01010107',
28                                   '01010110', '01010118', '01010604',
29                                   '01010603', '01010202', '01010203',
30                                   '01010205', '01010206', '01010207',
31                                   '01010301', '01010302', '01010303',
32                                   '01010304', '01010308', '01010305',
33                                   '01010306', '01010310', '01010309',
34                                   '01010407', '01010401', '01010403',
35                                   '01010404', '01010406', '01010408',
36                                   '01010409', '01010410', '01010411',
37                                   '01010506', '01010502', '01010503',
38                                   '01010507', '01010509', '01011999',
39                                   '06636E7E_F243_4CAE_86AB_723CFBA1513C',
40                                   '06636E7E_F243_4CAE_86AB_723CFBA1513C',
41                                   '0741CB62_C25A_4B57_B0A9_2236BA8BAE97',
42                                   '0741CB62_C25A_4B57_B0A9_2236BA8BAE97',
43                                   '458E20D3_51B9_4A87_96B9_7005CAD462F0',
44                                   '458E20D3_51B9_4A87_96B9_7005CAD462F0',
45                                   '4B3F9D72_3427_4ECA_B089_78E2B9A76375',
46                                   '4B3F9D72_3427_4ECA_B089_78E2B9A76375',
47                                   '945F14D1_717B_4434_89AF_E4DF11E03617',
48                                   '945F14D1_717B_4434_89AF_E4DF11E03617',
49                                   'C035C1A7_7612_421C_800B_690FDB4E4E44',
50                                   'C035C1A7_7612_421C_800B_690FDB4E4E44',
51                                   'C6E3E43E_19EF_43E6_88B8_6C9DFC4363A1',
52                                   'C6E3E43E_19EF_43E6_88B8_6C9DFC4363A1',
53                                   'F7BFE91E_D79B_48BD_8233_E92A63A01921',
54                                   'F7BFE91E_D79B_48BD_8233_E92A63A01921' )
55         GROUP BY Application )
56         AND ( CASE WHEN temp2.UserCount IS NULL THEN 0 + 17
57                    ELSE temp2.UserCount + 0 + 17
58               END ) > a.LicenseUserCount

 SQL Server 执行时间:
   CPU 时间 = 0 毫秒,占用时间 = 0 毫秒。
警告: 由于使用了本地联接提示,联接次序得以强制实施。
SQL Server 分析和编译时间: 
   CPU 时间 = 46 毫秒,占用时间 = 58 毫秒。

(1 行受影响)
表 'Worktable'。扫描计数 1,逻辑读取 109 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'myFunction'。扫描计数 2,逻辑读取 58 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'myApplication'。扫描计数 1,逻辑读取 2 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'myUser'。扫描计数 1,逻辑读取 163 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'myUserRoles'。扫描计数 1,逻辑读取 73 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'myUserRights'。扫描计数 1,逻辑读取 8236 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

(1 行受影响)

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

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

 

我在连接那里加上了一个HASH,连接有三种方式,强制HASH后,我发现速度大幅度提高了,IO下来了,这里给出了警告,这里加HASH,是为了验证上面我所

说的,SQL的引擎选错了执行计划,导致执行时间过长,那么既然知道了这个问题,接下来找出有问题的点,这在优化非常长语句的时候,往往非常有效,我们

选取一部分SQL语句,从优化片段的方式,达到优化整个SQL语句,那么有问题的SQL,出在哪里?我想很多人能够看出来

 1 SELECT    temp.Application ,
 2                     COUNT(*) AS UserCount
 3           FROM      ( SELECT    f.Application
 4                       FROM      myUserRights ur
 5                                 INNER JOIN myFunction f ON ur.ObjectType = f.FunctionCode
 6                                 INNER JOIN myUser u ON ur.UserGUID = u.UserGUID
 7                       WHERE     u.IsAdmin = 0
 8                                 AND ( IsDisabeld = 0
 9                                       OR IsDisabeld IS NULL
10                                     )
11                                 AND ur.UserGUID NOT IN (
12                                 SELECT  UserGUID
13                                 FROM    myUserRoles
14                                 WHERE   RoleGUID = 'dbf6cef3-5dc1-4b21-9865-4664849eac78' )
15                       GROUP BY  f.Application ,
16                                 ur.UserGUID
17                     ) AS temp
18           GROUP BY  temp.Application

这里因为NOT IN的原因,导致了SQL的引擎选择了错误的执行计划,想想看,如果改用连接,那么SQL的引擎会自动选择比较优秀的连接方式,如果使用in,not

in当语句复杂的时候,执行计划往往是嵌套循环的连接方式,这也解释了为什么,我们提倡使用连接,而不是子查询.所以这里我们只需要修改NOT IN即可

 

如何优化

 1  SELECT COUNT(*)
 2  FROM   ( 
 3 SELECT    temp.Application ,
 4                     COUNT(*) AS UserCount
 5           FROM      ( SELECT    f.Application
 6                       FROM      myUserRights ur
 7                                 INNER JOIN myFunction f ON ur.ObjectType = f.FunctionCode
 8                                 INNER JOIN myUser u ON ur.UserGUID = u.UserGUID
 9                           LEFT JOIN (SELECT distinct    UserGUID
10                                 FROM    myUserRoles
11                                 WHERE   RoleGUID = 'dbf6cef3-5dc1-4b21-9865-4664849eac78')
12                                  m ON ur.UserGUID = m.UserGUID 
13                     
14                       WHERE     u.IsAdmin = 0
15                                 AND ( IsDisabeld = 0
16                                       OR IsDisabeld IS NULL
17                                     )
18                                     AND  m.UserGUID IS NULL
19                              
20                              
21                       GROUP BY  f.Application ,
22                                 ur.UserGUID
23                     ) AS temp
24           GROUP BY  temp.Application
25         ) AS temp2
26         RIGHT   JOIN myApplication a ON a.Application = temp2.Application
27  WHERE  a.Level = 1
28         AND a.Application IN (
29         SELECT  Application
30         FROM    myFunction
31         WHERE   FunctionCode IN ( '', '01010102', '01010104', '01010105',
32                                   '01010111', '01010106', '01010107',
33                                   '01010110', '01010118', '01010604',
34                                   '01010603', '01010202', '01010203',
35                                   '01010205', '01010206', '01010207',
36                                   '01010301', '01010302', '01010303',
37                                   '01010304', '01010308', '01010305',
38                                   '01010306', '01010310', '01010309',
39                                   '01010407', '01010401', '01010403',
40                                   '01010404', '01010406', '01010408',
41                                   '01010409', '01010410', '01010411',
42                                   '01010506', '01010502', '01010503',
43                                   '01010507', '01010509', '01011999',
44                                   '06636E7E_F243_4CAE_86AB_723CFBA1513C',
45                                   '06636E7E_F243_4CAE_86AB_723CFBA1513C',
46                                   '0741CB62_C25A_4B57_B0A9_2236BA8BAE97',
47                                   '0741CB62_C25A_4B57_B0A9_2236BA8BAE97',
48                                   '458E20D3_51B9_4A87_96B9_7005CAD462F0',
49                                   '458E20D3_51B9_4A87_96B9_7005CAD462F0',
50                                   '4B3F9D72_3427_4ECA_B089_78E2B9A76375',
51                                   '4B3F9D72_3427_4ECA_B089_78E2B9A76375',
52                                   '945F14D1_717B_4434_89AF_E4DF11E03617',
53                                   '945F14D1_717B_4434_89AF_E4DF11E03617',
54                                   'C035C1A7_7612_421C_800B_690FDB4E4E44',
55                                   'C035C1A7_7612_421C_800B_690FDB4E4E44',
56                                   'C6E3E43E_19EF_43E6_88B8_6C9DFC4363A1',
57                                   'C6E3E43E_19EF_43E6_88B8_6C9DFC4363A1',
58                                   'F7BFE91E_D79B_48BD_8233_E92A63A01921',
59                                   'F7BFE91E_D79B_48BD_8233_E92A63A01921' )
60         GROUP BY Application )
61         AND ( CASE WHEN temp2.UserCount IS NULL THEN 0 + 17
62                    ELSE temp2.UserCount + 0 + 17
63               END ) > a.LicenseUserCount
64               
65               
(1 行受影响)
表 'Worktable'。扫描计数 1,逻辑读取 109 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'myUserRights'。扫描计数 1905,逻辑读取 15739 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'myFunction'。扫描计数 2,逻辑读取 58 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'myUser'。扫描计数 1,逻辑读取 163 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'myUserRoles'。扫描计数 1,逻辑读取 73 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'myApplication'。扫描计数 1,逻辑读取 2 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

(1 行受影响)

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

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

我们把not in改成连接后,性能得到了本质的提升,这种巨大的差异,有时候在工作上往往让我得意一会。

招聘信息

1. 工作地点:武汉 

2. 工作经验2年以上

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

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

给我发邮件:236773862@qq.com 

posted on 2013-04-22 16:44  zhourui  阅读(1842)  评论(9编辑  收藏  举报