【问题标题】:Oracle Explain Plan for Query with Collection using Table functionOracle Explain Plan for Query with Collection using Table function
【发布时间】:2012-05-04 18:25:54
【问题描述】:

如何在Oracle中使用表函数在exists子句中使用一些Collection为查询生成计划

例如

CREATE TYPE TYP_EMP AS OBJECT(
EMPNO     NUMBER(4),
ENAME     VARCHAR2(10),
JOB       VARCHAR2(9),
MGR       NUMBER(4),
HIREDATE  DATE,
SAL       NUMBER(7,2),
COMM      NUMBER(7,2),
DEPTNO    NUMBER(2));
/

类型已创建。

CREATE TYPE TYP_EMP_COLL AS TABLE OF TYP_EMP;
/

类型已创建。

CREATE OR REPLACE VIEW VW_EMP 
OF TYP_EMP
WITH OBJECT IDENTIFIER(EMPNO) AS
SELECT
EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM, DEPTNO
FROM EMP
/

视图已创建

CREATE TYPE TYP_DEPT AS OBJECT(
DEPTNO NUMBER(2),
DNAME  VARCHAR2(14),
LOC    VARCHAR2(13));
/

类型创建

CREATE TYPE TYP_DEPT_COLL AS TABLE OF TYP_DEPT;
/

类型创建

CREATE OR REPLACE VIEW VW_DEPT
OF TYP_DEPT
WITH OBJECT IDENTIFIER(DEPTNO) AS
SELECT
DEPTNO, DNAME, LOC
FROM DEPT
/

CREATE OR REPLACE PROCEDURE SP_EMPTEST AS
V_EMP TYP_EMP_COLL;
V_DEPT TYP_DEPT_COLL;
BEGIN
SELECT VALUE(V) BULK COLLECT INTO V_EMP FROM VW_EMP V;
SELECT VALUE(VD)
BULK COLLECT INTO V_DEPT FROM VW_DEPT VD
WHERE EXISTS
    (SELECT 1 FROM TABLE(V_EMP) VV WHERE VD.DEPTNO = VV.DEPTNO);
END;
/

跟踪文件的TKPROF输出

********************************************************************************

SQL ID: 7c02yjs9q5kqr
Plan Hash: 2616009478
SELECT VALUE(VD) 
FROM
VW_DEPT VD WHERE EXISTS (SELECT 1 FROM TABLE(:B1 ) VV WHERE VD.DEPTNO = VV.DEPTNO)


call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.00       0.00          0          0          0           0
Execute      1      0.00       0.00          0         19          0           0
Fetch        1      0.00       0.00          0          7          0           3
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        3      0.00       0.00          0         26          0           3

Misses in library cache during parse: 1
Misses in library cache during execute: 1
Optimizer mode: ALL_ROWS
Parsing user id: 84  (mac)   (recursive depth: 1)

Rows     Row Source Operation
-------  ---------------------------------------------------
  3  HASH JOIN SEMI (cr=7 pr=0 pw=0 time=6 us cost=33 size=32 card=1)
  4   TABLE ACCESS FULL DEPT (cr=7 pr=0 pw=0 time=0 us cost=3 size=120 card=4)
 14   COLLECTION ITERATOR PICKLER FETCH (cr=0 pr=0 pw=0 time=0 us cost=29 size=28 card=14)

error during execute of EXPLAIN PLAN statement
ORA-22905: cannot access rows from a non-nested table item

parse error offset: 129

Elapsed times include waiting on following events:
Event waited on                             Times   Max. Wait  Total Waited
----------------------------------------   Waited  ----------  ------------
asynch descriptor resize                        1        0.00          0.00
********************************************************************************

【问题讨论】:

  • 您是否尝试过将查询计划解释为“普通”查询?结果如何?
  • 集合变量出现错误'ORA-22905: cannot access rows from a non-nested table item'
  • 嗨,杰弗里,请查看已编辑帖子中的示例
  • 我不是 100% 了解 Oracle 中的类型,但在我看来,VW_DEPT 视图需要在 SELECT 中强制转换为 TYP_DEPT - 即 SELECT TYP_DEPT(DEPTNO, DNAME, LOC)从部门。 TYP_DEPT 也可能需要适当的 CONSTRUCTOR。
  • @Bob Jarvis - 没关系,因为上述过程执行没有任何问题。但我只是想提高性能

标签: sql oracle optimization sql-execution-plan


【解决方案1】:

有许多不同的方法可以得到解释计划。这不仅仅是一个详尽的列表。我认为这些方法之间存在重要差异,值得将它们中的每一个都包含在您的工具包中。

1 - 跟踪对过程的调用。

正如您的错误所示,将 SQL 从 PL/SQL 块中提取出来可能会很棘手。尽管 PL/SQL 和 SQL 旨在协同工作,但它们之间的交互有时会很奇怪。

跟踪过程肯定会得到正确的上下文,并且还可能向您展示正在发生的其他重要事情。虽然我通常会避免跟踪,因为它会创建太多无用的信息,而且通常(莫名其妙地)难以访问服务器。

2 - 实例化 SQL 中的类型

创建一个空集合,然后你的常规解释计划方法应该可以工作:

SELECT VALUE(VD) 
FROM
VW_DEPT VD WHERE EXISTS (SELECT 1 FROM TABLE(typ_emp_coll()  ) VV WHERE VD.DEPTNO = VV.DEPTNO);

或填充实际值:

SELECT VALUE(VD) 
FROM
VW_DEPT VD WHERE EXISTS (SELECT 1 FROM TABLE(typ_emp_coll(typ_emp(null,null,null,null,null,null,null,null))  ) VV WHERE VD.DEPTNO = VV.DEPTNO);

用有意义的值填充它可能需要一些大量的 SQL 语句。但有时将所有东西都放在一个地方会很有帮助。此外,正如我稍后将解释的,是否使用空集合实际上并不重要。 Oracle 无法区分空集合和大集合。

3 - 使用执行查询的 SQL_ID。

V$SQL 中查找 SQL_ID。这可能返回超过 1 行,您可能需要手动选择正确的行。

select * from v$sql where upper(sql_text) like '%SELECT 1 FROM TABLE(%';

sql_id可以通过多种方式获取plan,比如:

select * from table(dbms_xplan.display_cursor(sql_id => '7c02yjs9q5kqr'));

SQL_ID  7c02yjs9q5kqr, child number 0
-------------------------------------
SELECT VALUE(VD) FROM VW_DEPT VD WHERE EXISTS (SELECT 1 FROM TABLE(:B1 
) VV WHERE VD.DEPTNO = VV.DEPTNO)

Plan hash value: 2616009478

-------------------------------------------------------------------------------------------
| Id  | Operation                          | Name | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                   |      |       |       |    32 (100)|          |
|*  1 |  HASH JOIN SEMI                    |      |     1 |    32 |    32   (4)| 00:00:01 |
|   2 |   TABLE ACCESS FULL                | DEPT |     1 |    30 |     2   (0)| 00:00:01 |
|   3 |   COLLECTION ITERATOR PICKLER FETCH|      |  8168 | 16336 |    29   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------

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

   1 - access("DEPTNO"=SYS_OP_ATG(VALUE(KOKBF$),8,9,2))

有很多相关的方法可以获得相似的信息:使用/*+ gather_plan_statistics */提示、v$sql_monitorv$sql_plandbms_sqltune.report_sql_monitor(sql_id => '7c02yjs9q5kqr', type => 'active')等。每种方法都有不同的优缺点。就个人而言,我尽可能使用report_sql_monitor。这是查询的一个很好的图形表示, 虽然它需要一些额外的许可。

4 - 使用函数而不是变量。

create or replace package test_pkg is
    v_emp typ_emp_coll;
    function get_v_emp return typ_emp_coll;
end;
/

create or replace package body test_pkg is
    function get_v_emp return typ_emp_coll is
    begin
        return v_emp;
    end;
end;
/

现在您可以将查询作为独立的 SQL 语句运行,并使用真实的集合数据。您的正常解释计划方法应该可以正常工作:

select * from table(test_pkg.get_v_emp);

为什么计划总是那么糟糕?

无论您使用哪种方法,您可能会看到与我生成的几乎相同的解释计划。这是因为函数和集合对优化器是不可见的,它每次只会猜测8168 行。

要调整这些查询,您可能需要查看这篇文章,setting cardinality for pipelined and table functions

这是一个使用动态采样的快速解决方案示例:

select /*+ dynamic_sampling(5) */ * from table(test_pkg.get_v_emp)

【讨论】:

    猜你喜欢
    • 2012-08-13
    • 2013-01-01
    • 1970-01-01
    • 2018-04-30
    • 2013-07-13
    • 1970-01-01
    • 2011-12-30
    • 1970-01-01
    • 2022-08-22
    相关资源
    最近更新 更多