簡體   English   中英

Oracle 11gR2 - 查看功能列評估

[英]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)|
----------------------------------------------------------------------------------------

我完全理解第二個計划是做得少,但這並不能解釋為什么函數沒有被評估...至少不是我可以解決的。

ROWNUM分頁可以通過兩種方式執行:

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM