SQL優化之not in


一直從事運維的工作,免不了優化一些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 


免責聲明!

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



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