前兩天開發告訴我,項目數據遷移需要用到的一條sql在跑了幾分鐘後就會報錯。截圖給我是temp表空間撐滿了。sql拿過來看了一下執行計劃走了MERGE JOIN CARTESIAN。大多數情況下,笛卡爾join效率都非常差,出現錯誤的MERGE JOIN CARTESIAN原因主要是CBO估算錯誤或者sql語句忘記寫等值連接條件。此sql參與笛卡爾join的兩個結果集都非常大,所以耗盡temp表空間就不奇怪了。
仔細看看sql,發現sql雖然關聯比較複雜,但最終的結果集才1萬多條記錄。而且一個大的業務表上寫的兩個過濾條件選擇性都還不錯,所以想到了加上合適的hint,(統計信息已經是最新的了)讓選擇性不錯的表做驅動走nest loop應該5秒內就能出結果。
-----------------------------------------------
select
xxx from ooi,oosla,ooslb
where oosla.order_item_id = ooi.id
and oosla.gmt_service_begin > trunc(sysdate -
2)
or oosla.gmt_service_end > (sysdate -
3)
and
oosla.order_item_id = ooslb.order_item_id
and ooi.is_deleted = 'n'
and oosla.is_deleted =
'n'
and ooi.PRODUCT_CODE IN
('pc001', 'pc005', 'pc002', 'pc006', 'pc003','pc090', 'pc091',
'pc007')
ORDER BY ooslb.gmt_service_begin;
------------------------------------------------
圖中標紅的兩個條件上是有索引的,而且通過過濾後記錄數值有2萬多條,所以sql改寫成:
select xxx from ooi,
(select *
from oosla
where gmt_service_begin > trunc(sysdate - 2)or
gmt_service_end > (sysdate - 3)) oosla,
ooslb
where oosla.order_item_id = ooi.id
and oosla.order_item_id =
ooslb.order_item_id
and ooi.is_deleted = 'n'
and oosla.is_deleted =
'n'
and ooi.PRODUCT_CODE IN
('pc001', 'pc005', 'pc002', 'pc006', 'pc003','pc090', 'pc091',
'pc007')
ORDER BY ooslb.gmt_service_begin;
--------------------------------------------
oosla,ooi,ooslb走了nest loop,3秒不到就出來了。
在寫hint的時候,還發現了和index_combine相近的一個hint:index_join ,oracle在使用多個index返回結果集的時候其實還有一種方法:and_equal 有興趣的同學可以參考lewis的文章:
http://nixforums.org/about109312-Hints-difference-between-AND_EQUAL--INDEX_JOIN-and-INDEX_.html