原文:http://blog.csdn.net/liangweiwei130/article/details/37882503
-------------------------------------------------
在做項(xiàng)目的過程中,一個頁面使用類似如下的SQL查詢數(shù)據(jù),為了保密和使用方便,我把項(xiàng)目中有關(guān)的表名和字段替換使用ORACLE數(shù)據(jù)庫中的系統(tǒng)表和字段。
在我所做的項(xiàng)目中,類似ALL_TABLES的表中大概有8W多條數(shù)據(jù),下面這個查詢SQL很慢。
[sql]?view plain
?copy ?print?
WITH?PARAMS?AS?? ?(SELECT?''?USER_ID,?''?SDATE,?'%'?||?''?||?'%'?SNAME?FROM?DUAL)?? SELECT?AU.USERNAME,?AU.USER_ID?? ??FROM?ALL_USERS?AU?? ?INNER?JOIN?PARAMS?PA?? ????ON?1?=?1?? ?INNER?JOIN?DBA_USERS?DU?? ????ON?AU.USERNAME?=?DU.USERNAME?? ?WHERE?((PA.SDATE?IS?NULL?AND?PA.USER_ID?IS?NOT?NULL?AND?? ???????AU.USER_ID?=?PA.USER_ID)?OR?? ????????? ???????(PA.SDATE?IS?NULL?AND?PA.USER_ID?IS?NULL?AND?? ???????AU.USERNAME?NOT?IN?? ???????(SELECT?AU.USERNAME?? ????????????FROM?ALL_USERS?AU?? ???????????INNER?JOIN?DBA_USERS?DEV?? ??????????????ON?AU.USERNAME?=?DEV.USERNAME?? ???????????INNER?JOIN?(SELECT?OWNER?AS?USERNAME?? ????????????????????????FROM?ALL_TABLES?T?? ???????????????????????WHERE?T.LAST_ANALYZED?=?TRUNC(SYSDATE))?ATA?? ??????????????ON?AU.USERNAME?=?ATA.USERNAME))?OR?? ???????(PA.SDATE?IS?NOT?NULL?AND?? ???????AU.USERNAME?IN?? ???????(SELECT?AU.USERNAME?? ????????????FROM?ALL_USERS?AU?? ???????????INNER?JOIN?DBA_USERS?PA?? ??????????????ON?AU.USERNAME?=?PA.USERNAME?? ???????????INNER?JOIN?ALL_TABLES?ATA?? ??????????????ON?PA.USERNAME?=?ATA.OWNER?? ???????????WHERE?TO_CHAR(ATA.LAST_ANALYZED,?'YYYY-MM-DD')?=?PA.SDATE)?AND?? ???????AU.USER_ID?=?PA.USER_ID))?? ???AND?DU.PROFILE?LIKE?'D%'?? ???AND?AU.USERNAME?LIKE?PA.SNAME??
針對上面的SQL語句執(zhí)行慢的問題,我做了如下的分析:
????????????????第一步,把語句的WHERE條件后的三個OR都分別和主查詢一塊執(zhí)行,執(zhí)行速度都很快,放到一塊就很慢。
????????????????第二步,對比上面SQL和三個OR拆分出來的三個SQL的執(zhí)行計(jì)劃,如下圖所示。發(fā)現(xiàn)上面SQL的執(zhí)行中有一個FILTER,過濾器謂詞中用到了NOT EXISTS,是導(dǎo)致這條SQL跑的慢的原因。
原因找到了,就得想辦法把執(zhí)行計(jì)劃的FILTER去掉。開始想加HINT,但是實(shí)驗(yàn)了很多HINT,都不起作用。最后的結(jié)果還一樣,后來想到WITH AS?能提高SQL的查詢速度,就把影響SQL執(zhí)行的那段SQL放到WITH AS里面,結(jié)果還是一樣。后來嘗試把HINT?MATERIALIZE和WITH AS?結(jié)合使用,修改成如下的SQL,查詢速度立即提升了很多。如下圖所示,執(zhí)行計(jì)劃中FILTER的NOT EXISTS不存在了。
[sql]?view plain
?copy ?print?
WITH?PARAMS?AS?? ?(SELECT?''?USER_ID,?''?SDATE,?'%'?||?''?||?'%'?SNAME?FROM?DUAL),?? USERNAMEDATA?AS?? ?(SELECT?/*+?materialize?*/?? ???AU.USERNAME?? ????FROM?ALL_USERS?AU?? ???INNER?JOIN?DBA_USERS?DEV?? ??????ON?AU.USERNAME?=?DEV.USERNAME?? ???INNER?JOIN?(SELECT?OWNER?AS?USERNAME?? ????????????????FROM?ALL_TABLES?T?? ???????????????WHERE?T.LAST_ANALYZED?=?TRUNC(SYSDATE))?ATA?? ??????ON?AU.USERNAME?=?ATA.USERNAME)?? SELECT?AU.USERNAME,?AU.USER_ID?? ??FROM?ALL_USERS?AU?? ?INNER?JOIN?PARAMS?PA?? ????ON?1?=?1?? ?INNER?JOIN?DBA_USERS?DU?? ????ON?AU.USERNAME?=?DU.USERNAME?? ?WHERE?((PA.SDATE?IS?NULL?AND?PA.USER_ID?IS?NOT?NULL?AND?? ???????AU.USER_ID?=?PA.USER_ID)?OR?? ????????? ???????(PA.SDATE?IS?NULL?AND?PA.USER_ID?IS?NULL?AND?? ???????AU.USERNAME?NOT?IN?(SELECT?USERNAME?FROM?USERNAMEDATA))?OR?? ???????(PA.SDATE?IS?NOT?NULL?AND?? ???????AU.USERNAME?IN?? ???????(SELECT?AU.USERNAME?? ????????????FROM?ALL_USERS?AU?? ???????????INNER?JOIN?DBA_USERS?PA?? ??????????????ON?AU.USERNAME?=?PA.USERNAME?? ???????????INNER?JOIN?ALL_TABLES?ATA?? ??????????????ON?PA.USERNAME?=?ATA.OWNER?? ???????????WHERE?TO_CHAR(ATA.LAST_ANALYZED,?'YYYY-MM-DD')?=?PA.SDATE)?AND?? ???????AU.USER_ID?=?PA.USER_ID))?? ???AND?DU.PROFILE?LIKE?'D%'?? ???AND?AU.USERNAME?LIKE?PA.SNAME??
總結(jié):
在FILTER中,NOT EXISTS后的SQL語句多次執(zhí)行,本來數(shù)據(jù)量就很大,每次都要執(zhí)行一遍,結(jié)果可想而知。但是使用HINT?MATERIALIZE和WITH AS?結(jié)合使用,把內(nèi)聯(lián)視圖實(shí)體化,執(zhí)行過程中會創(chuàng)建基于視圖的臨時(shí)表。這樣就不會每次NOT EXISTS都去執(zhí)行一遍大數(shù)據(jù)表的掃描,只需要掃描一次即可。
但是是不是可以在WITHAS中的每個語句都實(shí)體化那?如果WITH AS中的語句只被調(diào)用一次的話,最好還是不要使用HINT?MATERIALIZE,因?yàn)槭褂?/span>HINT?MATERIALIZE第一次查詢會創(chuàng)建基于視圖結(jié)果的臨時(shí)表,這也耗費(fèi)一些時(shí)間。多次使用的話可以使用HINT?MATERIALIZE。
總結(jié)
以上是生活随笔為你收集整理的ORACLE使用WITH AS和HINT MATERIALIZE优化SQL解决FILTER效率低下的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。