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