1. 程式人生 > >儘量減少union all 的使用

儘量減少union all 的使用

        通過我自己做報表,以及和大家的交流,我發現大家在報表中使用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>