2012-08-23 53 views
1

我在Oracle中創建了一個類似OLAP的包,您可以在其中調用一個主控制函數,該函數通過進行多次左連接來組裝其返回的輸出表。這些連接表在包中的'slave'函數中定義,它使用靜態遊標返回特定的子集,並由函數的參數進行參數化。事情是,這些遊標都非常相似。概括Oracle靜態遊標

有沒有一種方法,除了生成動態查詢並在ref cursor中使用它們,我可以概括這些。每當我添加一個函數時,作爲開發人員,我都會感到這種奇怪的感覺,這並不是特別優雅!


Pseduocode

somePackage 
    function go(param) 
    return select myRows.id, 
        stats1.value, 
        stats2.value 
      from  myRows 
      left join table(somePackage.stats1(param)) stats1 
      on  stats1.id = myRows.id 
      left join table(somePackage.stats2(param)) stats2 
      on  stats2.id = myRows.id 

    function stats1(param) 
    return [RESULTS OF SOME QUERY] 

    function stats2(param) 
    return [RESULTS OF A RELATED QUERY] 

stats查詢都具有相同的結構:

  • 首先它們聚集的數據以有用的方式
  • 然後他們這個數據分割成邏輯部分,根據標準並再次彙總(例如,按部門,按地區等),然後結合結果
  • 然後它們返回的結果,撒入相關object類型,所以我可以很容易地做一個bulk collect

喜歡的東西:

cursor myCursor is 
    with fullData as (
    [AGGREGATE DATA] 
), 
    fullStats as (
    [AGGREGATE FULLDATA BY TOWN] 
    union all 
    [AGGREGATE FULLDATA BY REGION] 
    union all 
    [AGGREGATE FULLDATA BY COUNTRY] 
) 
    select myObjectType(fullStats.*) 
    from fullStats; 

... 

open myCursor; 
fetch myCursor bulk collect into output limit 1000; 
close myCursor; 

return output; 
+2

是你的問題「如何在不生成動態查詢的情況下生成動態查詢?」 – Ben

+0

採取點!我的推理是我所有的查詢都有相同的結構:所以也許更精確的總結是'我可以創建多態遊標嗎?' – Xophmeister

回答

1

過濾操作可以幫助建立與靜態SQL動態查詢。特別是當列列表是靜態的時候。

您可能已經考慮過此方法,但出於性能原因將其丟棄。 「如果我們只需要從其中一個結果中獲得結果 ,爲什麼要執行每個SQL塊?」運氣好的話,優化程序已經爲您執行FILTER操作。

實例查詢

首先創建等待每次運行時間5秒的功能。它將幫助查找哪些查詢塊被執行。

create or replace function slow_function return number is begin 
    dbms_lock.sleep(5); 
    return 1; 
end; 
/

該靜態查詢由綁定變量控制。有三種查詢塊,但整個查詢在5秒內運行,而不是15

declare 
    v_sum number; 
    v_query1 number := 1; 
    v_query2 number := 0; 
    v_query3 number := 0; 
begin 
    select sum(total) 
    into v_sum 
    from 
    (
    select total from (select slow_function() total from dual) where v_query1 = 1 
    union all 
    select total from (select slow_function() total from dual) where v_query2 = 1 
    union all 
    select total from (select slow_function() total from dual) where v_query3 = 1 
); 
end; 
/

執行計劃

這表現並不好運氣的結果;這不僅僅是Oracle在另一個之前隨機地執行一個謂詞。 Oracle在運行時分析綁定變量,甚至不執行不相關的查詢塊。這就是以下FILTER的操作。 (這是一個不好的名字,很多人通常把所有的謂詞稱爲「過濾器」,但是其中只有一部分會導致FILTER的操作。)

select * from table(dbms_xplan.display_cursor(sql_id => '0cfqc6a70kzmt')); 

SQL_ID 0cfqc6a70kzmt, child number 0 
------------------------------------- 
SELECT SUM(TOTAL) FROM (SELECT TOTAL FROM (SELECT SLOW_FUNCTION() 
TOTAL FROM DUAL) WHERE :B1 = 1 UNION ALL SELECT TOTAL FROM (SELECT 
SLOW_FUNCTION() TOTAL FROM DUAL) WHERE :B2 = 1 UNION ALL SELECT TOTAL 
FROM (SELECT SLOW_FUNCTION() TOTAL FROM DUAL) WHERE :B3 = 1) 

Plan hash value: 926033116 

------------------------------------------------------------------------- 
| Id | Operation  | Name | Rows | Bytes | Cost (%CPU)| Time  | 
------------------------------------------------------------------------- 
| 0 | SELECT STATEMENT |  |  |  |  6 (100)|   | 
| 1 | SORT AGGREGATE |  |  1 | 13 |   |   | 
| 2 | VIEW   |  |  3 | 39 |  6 (0)| 00:00:01 | 
| 3 | UNION-ALL  |  |  |  |   |   | 
|* 4 |  FILTER  |  |  |  |   |   | 
| 5 |  FAST DUAL |  |  1 |  |  2 (0)| 00:00:01 | 
|* 6 |  FILTER  |  |  |  |   |   | 
| 7 |  FAST DUAL |  |  1 |  |  2 (0)| 00:00:01 | 
|* 8 |  FILTER  |  |  |  |   |   | 
| 9 |  FAST DUAL |  |  1 |  |  2 (0)| 00:00:01 | 
------------------------------------------------------------------------- 

Predicate Information (identified by operation id): 
--------------------------------------------------- 

    4 - filter(:B1=1) 
    6 - filter(:B2=1) 
    8 - filter(:B3=1) 

問題

FILTER操作不良記錄。我無法詳細解釋它何時有效或無效,以及它究竟影響了查詢的其他部分。例如,在解釋計劃的Rows估計是3,但在運行時,甲骨文應該能夠輕鬆地估計基數爲1。顯然,執行計劃是不是動態的,即基數估計差可能會導致以後的問題。另外,我看到了一些奇怪的情況,靜態表達式沒有被適當地過濾。但是如果查詢使用簡單的相等謂詞,它應該沒問題。

這種方法允許你刪除所有動態SQL,並用大的靜態SQL語句替換它。這有一些優點;動態SQL通常是「醜陋」的,難以調試。但只熟悉程序編程的人往往會認爲單個SQL語句是一個巨大的上帝功能,這是一種不好的做法。他們不會明白,UNION ALL S創建SQL的獨立塊

動態SQL仍可能會更好

一般來說,我會建議這種方法。你有什麼好,因爲它看起來好。動態SQL的主要問題是人們不把它當作真正的代碼來對待;它沒有被評論或格式化,最終看起來像一個沒有人能理解的可怕混亂。如果你能夠花費額外的時間來生成乾淨的代碼,那麼你應該堅持。

+0

有趣的:)說實話,自從這個問題出現以來一直這麼久,我甚至不知道我用什麼解決方案。我認爲我堅持使用類似的函數,並且與代碼重複一起生活,而不是動態SQL生成,因爲它更快(即,靜態遊標被優化)。但是,瞭解優化器的優點總是很有趣的!謝謝 :) – Xophmeister