儘量減少union all 的使用
阿新 • • 發佈:2018-12-30
通過我自己做報表,以及和大家的交流,我發現大家在報表中使用union all 還是比較多的. 一般情況下,因為需要不同的條件,關聯不同的表,所以寫起union all 語句來還是很方便,但是想把這些邏輯都在一個語句中實現卻有些困難. 而且,大家普遍認為,因為union all 中是有條件的,因此執行時,並不是整個union all 語句在執行,而是 union all 的部分語句在執行而已.
我認為這個其實是個錯誤的概念: 因為 所謂的條件也是在sql 語句中的一個條件, oralce 沒法根據某個where 條件來判斷是否不執行一段語句, 實際上,oracle 會完全按照所得出的執行計劃來執行,控制的條件肯定會被執行到的,但是可能之前已經執行了很多沒必要的操作,即使在所在的語句塊中,控制條件能被早先執行到,如果控制的條件不是返回false,後面的條件仍然會被執行,這個時候,表仍然在被掃描,巢狀仍然在繼續, 所以這種重複執行的代價是非常大的,因此 建議儘量減少使用union all 的使用,而是儘可能的把語句簡單化, 可以採用如下的寫法來避免union all 的過多使用.
( ( constant_condition1 and condition1 )
or
( constant_condition2 and condition2)
)
constant_condition1 和 constant_condition2 是 常量表達式,這裡即為我們的控制條件;
condition1 和condition2 分別為 constant_condition1 和 constant_condition2 常量條件下的 條件.
一般來說,幾個常量表達式的合集應該是個全集.
比如,為了控制 科目明細帳的單幣種和多幣種邏輯, 我只需要 加入這樣的判斷即可,而不用 寫兩個語句來union all. 其中 cp_currency_code_flag 是我定義的一個佔位列,我在before report 中會根據邏輯來給這個變數來賦值.
and (
( :cp_currency_code_flag = 'SINGLE-CODE' and gjh.currency_code = :p_currency_code )
OR
( :cp_currency_code_flag != 'SINGLE-CODE' )
)
附錄:
如下的例子大致反映出union all 重複執行的代價.
我做了個對比,下面兩個語句的邏輯意思實際上是一樣的,但是用了union all 之後,要多訪問一次表, 結果邏輯讀的代價是第一個語句的2倍. 解釋union all 的下半句返回為空.
SQL> select count(*) from test2 where object_name like 'F%';
COUNT(*)
---------
34
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 SORT (AGGREGATE)
2 1 TABLE ACCESS (FULL) OF 'TEST2'
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
134 consistent gets
0 physical reads
0 redo size
182 bytes sent via SQL*Net to client
251 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
SQL> select count(*) from test2 where object_name like 'F%' and object_id <= 300000
2 union all
3 select count(*) from test2 where object_name like 'F%' and object_id > 300000
COUNT(*)
---------
34
0
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 UNION-ALL
2 1 SORT (AGGREGATE)
3 2 TABLE ACCESS (FULL) OF 'TEST2'
4 1 SORT (AGGREGATE)
5 4 TABLE ACCESS (FULL) OF 'TEST2'
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
268 consistent gets
0 physical reads
0 redo size
195 bytes sent via SQL*Net to client
251 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
2 rows processed
SQL>
我認為這個其實是個錯誤的概念: 因為 所謂的條件也是在sql 語句中的一個條件, oralce 沒法根據某個where 條件來判斷是否不執行一段語句, 實際上,oracle 會完全按照所得出的執行計劃來執行,控制的條件肯定會被執行到的,但是可能之前已經執行了很多沒必要的操作,即使在所在的語句塊中,控制條件能被早先執行到,如果控制的條件不是返回false,後面的條件仍然會被執行,這個時候,表仍然在被掃描,巢狀仍然在繼續, 所以這種重複執行的代價是非常大的,因此 建議儘量減少使用union all 的使用,而是儘可能的把語句簡單化, 可以採用如下的寫法來避免union all 的過多使用.
( ( constant_condition1 and condition1 )
or
( constant_condition2 and condition2)
)
constant_condition1 和 constant_condition2 是 常量表達式,這裡即為我們的控制條件;
condition1 和condition2 分別為 constant_condition1 和 constant_condition2 常量條件下的 條件.
一般來說,幾個常量表達式的合集應該是個全集.
比如,為了控制 科目明細帳的單幣種和多幣種邏輯, 我只需要 加入這樣的判斷即可,而不用 寫兩個語句來union all. 其中 cp_currency_code_flag 是我定義的一個佔位列,我在before report 中會根據邏輯來給這個變數來賦值.
and (
( :cp_currency_code_flag = 'SINGLE-CODE' and gjh.currency_code = :p_currency_code )
OR
( :cp_currency_code_flag != 'SINGLE-CODE' )
)
附錄:
如下的例子大致反映出union all 重複執行的代價.
我做了個對比,下面兩個語句的邏輯意思實際上是一樣的,但是用了union all 之後,要多訪問一次表, 結果邏輯讀的代價是第一個語句的2倍. 解釋union all 的下半句返回為空.
SQL> select count(*) from test2 where object_name like 'F%';
COUNT(*)
---------
34
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 SORT (AGGREGATE)
2 1 TABLE ACCESS (FULL) OF 'TEST2'
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
134 consistent gets
0 physical reads
0 redo size
182 bytes sent via SQL*Net to client
251 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
SQL> select count(*) from test2 where object_name like 'F%' and object_id <= 300000
2 union all
3 select count(*) from test2 where object_name like 'F%' and object_id > 300000
COUNT(*)
---------
34
0
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 UNION-ALL
2 1 SORT (AGGREGATE)
3 2 TABLE ACCESS (FULL) OF 'TEST2'
4 1 SORT (AGGREGATE)
5 4 TABLE ACCESS (FULL) OF 'TEST2'
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
268 consistent gets
0 physical reads
0 redo size
195 bytes sent via SQL*Net to client
251 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
2 rows processed
SQL>