[英]Oracle 11gR2 - View Function Columns Evaluation
對於具有為列定義的函數以及何時評估這些函數的Oracle視圖,我似乎有一個奇怪的問題。
假設我有以下視圖和函數定義:
CREATE OR REPLACE VIEW test_view_one AS
SELECT column_one,
a_package.function_that_returns_a_value(column_one) function_column
FROM a_table;
CREATE OR REPLACE PACKAGE BODY a_package AS
FUNCTION function_that_returns_a_value(p_key VARCHAR2) RETURN VARCHAR2 IS
CURSOR a_cur IS
SELECT value
FROM table_b
WHERE key = p_key;
p_temp VARCHAR2(30);
BEGIN
-- Code here to write into a temp table. The function call is autonomous.
OPEN a_cur;
FETCH a_cur INTO p_temp;
CLOSE a_cur;
RETURN p_temp;
END function_that_returns_a_value;
END a_package;
通常,我希望如果function_column包含在查詢中,那么對於該查詢帶回的每一行,該函數都將運行。 這在某些情況下似乎是正確的,但對其他情況則不然。
例如,假設我有以下內容:
SELECT pageouter,*
FROM(WITH page_query AS (SELECT *
FROM test_view_one
ORDER BY column_one)
SELECT page_query.*, ROWNUM as innerrownum
FROM page_query
WHERE rownum <= 25) pageouter WHERE pageouter.innerrownum >= 1
在這種情況下,該內部查詢(查詢test_view_one)會帶回大約90,000條記錄。 如果我將函數定義為插入臨時表,那么我可以告訴該函數運行25次,每次返回一次。 正是我所期待的。
但是,如果我在該內部查詢中添加一個重要的where子句,例如
SELECT pageouter,*
FROM(WITH page_query AS (SELECT *
FROM test_view_one
WHERE EXISTS (SELECT 'x' FROM some_table WHERE ...)
AND NOT EXISTS (SELECT 'x' FROM some_other_table WHERE ...)
AND EXISTS (SELECT 'x' FROM another_table WHERE ...)
ORDER BY column_one)
SELECT page_query.*, ROWNUM as innerrownum
FROM page_query
WHERE rownum <= 25) pageouter WHERE pageouter.innerrownum >= 1
然后內部查詢返回的行數是60,000,如果我然后查詢臨時表,我可以告訴該函數已經運行了60,000次。 不出所料,這幾乎破壞了查詢的性能。
上面的查詢作為分頁實現的一部分運行,這就是為什么我們只返回25行,這就是為什么我們只需要為這25行運行函數的原因。
我應該補充一下,如果我更改了WHERE子句(即我刪除了一些條件),那么查詢會返回到自己的行為,只運行實際帶回的25行的函數。
有沒有人知道評估視圖中的函數的時間? 或者無論如何確定導致它的原因或者確定何時評估功能的方法(我已經檢查了解釋計划,並且那里似乎沒有任何東西可以放棄它)。 如果我知道那么我可以找到問題的解決方案,但似乎除了“他們將為每一行帶回來運行”之外的文檔很少,這在某些情況下顯然不是這種情況。
我完全理解,如果沒有工作模式,很難弄清楚正在發生什么,但如果您需要更多信息,請隨時詢問。
非常感謝
要求的附加信息。
以下是我從生產環境中獲得的實際解釋計划。 表名與上面的查詢不匹配(實際上涉及的表要多得多,但它們都是由WHERE子句中的NOT EXISTS語句加入的。)DEMISE表相當於上面查詢中的A_TABLE。
值得注意的是,在我運行解釋計划之前收集統計數據以使其盡可能准確。
我對此的理解是VIEW行是評估函數的位置,這些函數在行被過濾之后發生。 我的理解顯然有缺陷!
所以這是一個糟糕的計划,將該功能稱為60,000次...
Execution Plan
----------------------------------------------------------
-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 5 | 10230 | 984 (1)|
| 1 | FAST DUAL | | 1 | | 2 (0)|
| 2 | FAST DUAL | | 1 | | 2 (0)|
|* 3 | VIEW | | 5 | 10230 | 984 (1)|
|* 4 | COUNT STOPKEY | | | | |
| 5 | VIEW | | 5 | 10165 | 984 (1)|
|* 6 | SORT ORDER BY STOPKEY | | 5 | 340 | 984 (1)|
| 7 | COUNT | | | | |
|* 8 | FILTER | | | | |
|* 9 | HASH JOIN RIGHT OUTER | | 5666 | 376K| 767 (1)|
|* 10 | INDEX RANGE SCAN | USERDATAI1 | 1 | 12 | 2 (0)|
|* 11 | HASH JOIN RIGHT ANTI | | 5666 | 309K| 765 (1)|
|* 12 | INDEX FAST FULL SCAN | TNNTMVINI1 | 1 | 17 | 35 (0)|
|* 13 | HASH JOIN RIGHT ANTI | | 6204 | 236K| 729 (1)|
|* 14 | INDEX RANGE SCAN | CODESGENI3 | 1 | 10 | 2 (0)|
|* 15 | INDEX FULL SCAN | DEMISEI4 | 6514 | 184K| 727 (1)|
| 16 | NESTED LOOPS | | 1 | 25 | 3 (0)|
| 17 | NESTED LOOPS | | 1 | 25 | 3 (0)|
|* 18 | INDEX RANGE SCAN | PROPERTY_GC | 1 | 15 | 2 (0)|
|* 19 | INDEX UNIQUE SCAN | CODESGENI1 | 1 | | 0 (0)|
|* 20 | TABLE ACCESS BY INDEX ROWID| CODESGEN | 1 | 10 | 1 (0)|
| 21 | TABLE ACCESS FULL | QCDUAL | 1 | | 3 (0)|
|* 22 | INDEX RANGE SCAN | DMSELEASI4 | 1 | 21 | 2 (0)|
|* 23 | INDEX RANGE SCAN | TNNTMVINI1 | 1 | 17 | 1 (0)|
| 24 | TABLE ACCESS FULL | QCDUAL | 1 | | 3 (0)|
-------------------------------------------------------------------------------------------
這是個好計划。 這會將函數調用25次,但是從where子句中刪除了一些不存在的語句。
Execution Plan
----------------------------------------------------------
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)|
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 25 | 54200 | 144 (0)|
| 1 | FAST DUAL | | 1 | | 2 (0)|
| 2 | FAST DUAL | | 1 | | 2 (0)|
|* 3 | VIEW | | 25 | 54200 | 144 (0)|
|* 4 | COUNT STOPKEY | | | | |
| 5 | VIEW | | 26 | 56030 | 144 (0)|
| 6 | COUNT | | | | |
|* 7 | FILTER | | | | |
| 8 | NESTED LOOPS ANTI | | 30 | 3210 | 144 (0)|
| 9 | NESTED LOOPS OUTER | | 30 | 2580 | 114 (0)|
| 10 | NESTED LOOPS ANTI | | 30 | 2220 | 84 (0)|
| 11 | NESTED LOOPS ANTI | | 32 | 1824 | 52 (0)|
| 12 | TABLE ACCESS BY INDEX ROWID| DEMISE | 130K| 5979K| 18 (0)|
| 13 | INDEX FULL SCAN | DEMISEI4 | 34 | | 3 (0)|
|* 14 | INDEX RANGE SCAN | CODESGENI3 | 1 | 10 | 1 (0)|
|* 15 | INDEX RANGE SCAN | TNNTMVINI1 | 1 | 17 | 1 (0)|
|* 16 | INDEX RANGE SCAN | USERDATAI1 | 1 | 12 | 1 (0)|
|* 17 | INDEX RANGE SCAN | DMSELEASI4 | 1 | 21 | 1 (0)|
| 18 | TABLE ACCESS FULL | QCDUAL | 1 | | 3 (0)|
----------------------------------------------------------------------------------------
我完全理解第二個計划是做得少,但這並不能解釋為什么函數沒有被評估...至少不是我可以解決的。
A)使用優化排序完全掃描行源(限於前N行)或
B)行源的索引訪問完全沒有排序
這里是案例A的簡化示例
SELECT *
FROM
(SELECT a.*,
ROWNUM rnum
FROM
( SELECT * FROM test_view_one ORDER BY id
) a
WHERE ROWNUM <= 25
)
WHERE rnum >= 1
相應的執行計划如下所示(請注意,我預先發布了列投影的一部分 - 我將很快解釋原因):
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 25 | 975 | | 1034 (1)| 00:00:01 |
|* 1 | VIEW | | 25 | 975 | | 1034 (1)| 00:00:01 |
|* 2 | COUNT STOPKEY | | | | | | |
| 3 | VIEW | | 90000 | 2285K| | 1034 (1)| 00:00:01 |
|* 4 | SORT ORDER BY STOPKEY| | 90000 | 439K| 1072K| 1034 (1)| 00:00:01 |
| 5 | TABLE ACCESS FULL | TEST | 90000 | 439K| | 756 (1)| 00:00:01 |
-----------------------------------------------------------------------------------------
Column Projection Information (identified by operation id):
-----------------------------------------------------------
...
3 - "A"."ID"[NUMBER,22], "A"."FUNCTION_COLUMN"[NUMBER,22]
4 - (#keys=1) "ID"[NUMBER,22], "MY_PACKAGE"."MY_FUNCTION"("ID")[22]
5 - "ID"[NUMBER,22]
在執行過程中,使用FULL SCAN訪問表,即所有記錄都是紅色的。 優化在SORT操作中: SORT ORDER BY STOPKEY意味着並非所有行都已排序,但只保留前25個並進行排序。
這里是案例B的執行計划
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 25 | 975 | 2 (0)| 00:00:01 |
|* 1 | VIEW | | 25 | 975 | 2 (0)| 00:00:01 |
|* 2 | COUNT STOPKEY | | | | | |
| 3 | VIEW | | 26 | 676 | 2 (0)| 00:00:01 |
|* 4 | INDEX RANGE SCAN| TEST_IDX | 26 | 130 | 2 (0)| 00:00:01 |
--------------------------------------------------------------------------------
這里只訪問了所需的25行,因此該函數不能被稱為N次。
現在重要的考慮因素, 在A情況下,函數可以,但不需要為每一行調用 。 我們怎么看?
答案在解釋計划的列投影中。
4 - (#keys=1) "ID"[NUMBER,22], "MY_PACKAGE"."MY_FUNCTION"("ID")[22]
相關的第4行顯示,在SORT操作中調用該函數,因此每行都調用該函數。 (排序獲取所有行)。
結論
我在11.2上的測試顯示,在A(具有SORT ORDER BY STOPKEY的FULL SCAN)的情況下,每行調用一次視圖函數。 我想唯一的解決方法是只選擇ID,限制結果,然后加入原始視圖以獲取函數值。
最后的筆記
我也在12.1中對此進行了測試,並在下面看到了列投影的變化。 該函數首先在VIEW(第3行)中計算,即兩種情況都能正常工作。
Column Projection Information (identified by operation id):
-----------------------------------------------------------
...
3 - "A"."ID"[NUMBER,22], "A"."FUNCTION_COLUMN"[NUMBER,22]
4 - (#keys=1) "ID"[NUMBER,22]
5 - "ID"[NUMBER,22]
當然在12c中,可以使用OFFSET的新功能 - FETCH NEXT。
祝好運!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.