如何使用表函数
使用某些Collection in exists子句在Oracle中为查询生成计划e.g。
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;
/
********************************************************************************
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
********************************************************************************
答案 0 :(得分:4)
有许多不同的方法可以获得解释计划。这不仅仅是一个详尽的清单。我认为这些方法之间存在重要差异,并且值得在您的工具包中使用它们。
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来获取计划,例如:
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_monitor
,v$sql_plan
,dbms_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)