2016-02-15 45 views
2

真的,這是兩個相關問題的Oracle SQL函數

問:爲什麼會運行查詢與函數更快的SELECT子句比在where子句

問題:將where子句從內聯視圖移動到外部查詢時,爲什麼內聯視圖需要更長時間?

我不打算轉儲整個查詢,因爲它有與我的工作相關的列和表,但基本上這是它。如果你需要一個工作的例子,我會寫一個類似於我正在做的SQLFiddle。

運行時間:117S, 返回:93條記錄

SELECT COL1, COL2, COL3 
FROM my_table 
WHERE [CONDITIONS...] 
and my_package.my_function(:bind_var, COL1) = 'Y'; 

如果我跑由它自己會是什麼綁定var和這將是COL1的一個值,將功能拿.06s。所以,

SELECT my_package.my_function(VAL1, VAL2) FROM DUAL; 

所以我改寫了這樣的查詢:

SELECT * 
FROM (
    SELECT COL1, COL2, COL3 
    FROM my_table 
    WHERE [CONDITIONS...] 
) temp_tbl 
WHERE my_package.my_function(:bind_var, COL1) = 'Y'; 

運行時間:116S, 返回:93條記錄

無功能的查詢接受〜3秒運行,但是對於93個記錄需要0.06s的函數運行需要〜116s是沒有意義的。

我試着看看如果將函數移到SELECT子句會發生什麼。

SELECT * 
FROM (
    SELECT COL1, COL2, COL3, my_package.my_function(:bind_var, COL1) as fn_indc 
    FROM my_table 
    WHERE [CONDITIONS...] 
) temp_tbl 
WHERE fn_indc = 'Y'; 

當我運行內聯視圖查詢它需要〜3秒鐘運行。當我添加WHERE fn_indc = 'Y';時,運行需要大約116秒。爲什麼要將函數從WHERE移到SELECT事件? CHAR的比較並不需要很長時間才能完成。另外,如果我創建了一個內聯視圖,它從函數中檢索了值並在外部查詢中執行了我的where條件,那麼會導致它運行更長時間?

+1

您是否看過這些案例的解釋計劃? –

+2

函數實際執行多少次?優化器可以按照預期效率最高的順序自由評估謂詞。我的猜測是,當查詢速度較慢時,查詢計劃涉及的調用次數超過93次。查詢快時,查詢計劃涉及在調用函數之前將結果集裁剪爲93行。你需要提供查詢計劃來驗證猜測。從理論上講,任何這些查詢都可能會在任何時間點產生快速計劃或慢速計劃。 –

+0

令人困惑的是解釋計劃告訴我,對於在WHERE子句中具有函數的查詢,成本較低。 'WHERE my_package.my_function(:bind_var,COL1)='Y';'的成本是1.不會讓外部查詢中的函數強制優化器先運行內聯視圖? – enigmasck

回答

1

的功能被在各種情況下執行多少次?

如果沒有看到查詢計劃,我會打賭,當其他謂詞首先被計算時,查詢會快速運行,在調用函數之前儘可能地減少結果集。當該函數僅被調用93次(不管其他任何謂詞都未消除的行需要多少額外的執行),查詢運行得很快。另一方面,如果函數在查詢計劃的較早前被調用,則會多次調用該函數 - 可能會對錶中的每一行調用一次,並且查詢返回的速度會更慢。您可以通過查看查詢計劃或使用某些工具來精確測量在不同情況下調用函數的次數來驗證此情況。

Oracle優化器可以根據統計數據以任何它認爲合適的順序自由評估謂詞。重寫查詢有可能會導致優化器選擇更好或更差的不同計劃。但是明天,優化器可以完全自由地改變想法,並對您發佈的任何變體使用較慢的計劃。當然,墨菲是這片土地的法律,優化者可能會等待最糟糕的時間來決定將查詢計劃放在你身上,因爲這會給你帶來最大的痛苦和痛苦。

如果優化程序認爲快速計劃和慢速計劃大致同樣昂貴,那可能意味着它認爲該函數要麼比評估的實際成本低得多,要麼比實際選擇性要好得多。糾正錯誤信念的最好方法是使用函數associate statistics。這可以讓您告訴優化器查詢的成本及其選擇性。反過來,這又會讓優化器做出更好的估計,並使得它可能會選擇更高效的計劃,而不管您如何編寫查詢(並且使計劃在將來更糟糕的情況下可能會發生更改)。

現在,您還可以通過編寫查詢來阻止優化程序通過使用提示合併謂詞或在內聯視圖中添加某些內容以阻止謂詞被推送。一個古老的竅門是將rownum放入內聯視圖中。當然,有些未來版本的優化器可能會足夠聰明,以確定rownum在這裏沒有做任何事情,並且可以安全地刪除。而且你需要爲下一個出現的人留下一個很長的評論,並想知道爲什麼你在查詢中沒有使用rownum

SELECT * 
FROM (
    SELECT COL1, COL2, COL3, rownum 
    FROM my_table 
    WHERE [CONDITIONS...] 
) temp_tbl 
WHERE my_package.my_function(:bind_var, COL1) = 'Y'; 
1

你並沒有給我們太多的信息,所以我猜...

下面的查詢也最有可能利用一些指標,因此它比FTS(全表掃描)的運行速度更快:

SELECT * 
FROM (
    SELECT COL1, COL2, COL3, my_package.my_function(:bind_var, COL1) as fn_indc 
    FROM my_table 
    WHERE [CONDITIONS...] 
) temp_tbl 
WHERE fn_indc = 'Y'; 

所以它會通過相應的索引訪問'my_table',那麼它將[my_package.my_function(:bind_var,COL1)]函數僅應用於屬於結果集的行(即通過過濾)

如果您沒有定義基於函數的i ndex甲骨文不能使用索引的查詢:

SELECT * 
FROM (
    SELECT COL1, COL2, COL3 
    FROM my_table 
    WHERE [CONDITIONS...] 
) temp_tbl 
WHERE my_package.my_function(:bind_var, COL1) = 'Y'; 

所以它具有下列功能:1。 FTS(全表掃描),用於MY_TABLE 2.每一行應用於過濾器:my_package。如果你想改變你的函數,所以它會返回[:bind_var]而不是期待它作爲參數,那麼你可以構建基於函數的索引,並使使用它,如下所示:

SELECT COL1, COL2, COL3 
FROM my_table 
WHERE [CONDITIONS...] 
and my_package.my_function(COL1) = :bind_var; 
0

回答我的問題的一部分:

問:爲什麼要搬家時內嵌視圖需要更長的時間在那裏從內嵌視圖的外部查詢 條款。

這是因爲嵌套查詢由sql優化器作爲外連接進行連接。所以我試圖通過讓其他條件在函數被sql優化器取消之前運行來實現。如果我想強制SQL優化運行我的內嵌視圖首先,我可以添加rownum >= 1

SELECT * 
FROM (
    SELECT COL1, COL2, COL3 
    FROM my_table 
    WHERE [CONDITIONS...] 
     and rownum >= 1 
) temp_tbl 
WHERE my_package.my_function(:bind_var, COL1) = 'Y'; 

https://blogs.oracle.com/optimizer/entry/optimizer_transformations_subquery_unesting_part_1