如何在Oracle SQL中有条件地联接表函数(该函数有一个主表行列作为参数),而不需要过多的函数调用?
我有以下问题: 我的基表中的每一行在下面的列表中都有一个标志列“flg”和一个函数参数列“arg”。如果N行中的标志为“Y”,则必须调用函数;之后,它将返回,比方说,一列实际上是一堆,但我将尽可能地简化。最后,行N应该转换为子表—函数返回的行N和列的完全联接。 如果标志为“N”,则第N行的结果应为该行本身加上该函数返回列中的NULL 下面是一个例子:如何在Oracle SQL中有条件地联接表函数(该函数有一个主表行列作为参数),而不需要过多的函数调用?,oracle,plsql,oracle11g,Oracle,Plsql,Oracle11g,我有以下问题: 我的基表中的每一行在下面的列表中都有一个标志列“flg”和一个函数参数列“arg”。如果N行中的标志为“Y”,则必须调用函数;之后,它将返回,比方说,一列实际上是一堆,但我将尽可能地简化。最后,行N应该转换为子表—函数返回的行N和列的完全联接。 如果标志为“N”,则第N行的结果应为该行本身加上该函数返回列中的NULL 下面是一个例子: create or replace package SIEBEL.TEST_PACKAGE as type ret_type is tab
create or replace package SIEBEL.TEST_PACKAGE as
type ret_type is table of number;
function testFunc(inp number)
return ret_type
pipelined;
end TEST_PACKAGE;
/
create or replace package body SIEBEL.TEST_PACKAGE is
function testFunc(inp number)
return ret_type
pipelined
is
i number;
begin
dbms_output.put_line('Function call, arg = ' || to_char(inp));
if (inp is null OR inp = 0) then
pipe row (null);
else
for i in 1..inp loop
pipe row (i);
end loop;
end if;
end testFunc;
end TEST_PACKAGE;
/
with base_table as
(
select 'Shall invoke' col0, 'Y' flg, '2' arg from dual
union
select 'Shall not invoke' col0, 'N' flg, '0' arg from dual
)
select * from base_table t0, table(siebel.test_package.testFunc(t0.arg)) t1;
它将返回我真正想要的:
COL0 | FLG | ARG | COLUMN_VALUE
--------------------------------------------
Shall invoke | Y | 2 | 1
Shall invoke | Y | 2 | 2
Shall not invoke | N | 0 |
问题是,即使对于'N'标志,函数仍然被调用-数据库输出将显示
Function call, arg = 2
Function call, arg = 0
如果在现实世界中,我有数百张带“Y”标志的记录,大约有100K张带“N”标志的记录。另外,我的实际函数要复杂得多,名称空间中有很多内容,因此每个函数调用在性能方面都是至关重要的
我想要的是示例的数据库输出:
Function call, arg = 2
我可以用它来实现它
with base_table as
(
select 'Shall invoke' col0, 'Y' flg, '2' arg from dual
union
select 'Shall not invoke' col0, 'N' flg, '0' arg from dual
)
select t.*, t1.column_value
from base_table t, table(SIEBEL.TEST_PACKAGE.testFunc(t.arg)) t1
where t.flg = 'Y'
union all
select t.*, null as column_value
from base_table t
where t.flg = 'N';
但随后所有的索引都变得毫无用处——每个“orderby”指令都需要花费大量时间才能完成
请帮助我实现所需的函数调用行为,并保存原始行顺序
如果有什么不清楚的,请随时问我
致以最良好的祝愿,
Alexey对于flag=N,如果您有条件地加入,则不会调用该函数
set serveroutput on
with base_table as
(
select 'Shall invoke' col0, 'Y' flg, '2' arg from dual
union
select 'Shall not invoke' col0, 'N' flg, '0' arg from dual
)
select * from base_table t0
left join table( test_package.testFunc(t0.arg) ) t1 on (t0.flg = 'Y');
脚本输出
Package created.
Package body created.
COL0 FLG ARG COLUMN_VALUE
---------------- --- --- ------------
Shall invoke Y 2 1
Shall invoke Y 2 2
Shall not invoke N 0
3 rows selected.
服务器输出:
Function call, arg = 2
要避免对flag='N'的函数调用,请过滤掉该行 在过滤器谓词中添加where flg='Y' 为了获得更好的性能,请在flg列上创建索引 编辑 要使'flg='N'的行也在输出中,可以进行外部联接,而不需要联合 比如说,
SQL> CREATE table base_table AS
2 select * from(
3 ( SELECT 'Shall invoke' col0, 'Y' flg, '2' arg FROM dual
4 UNION
5 SELECT 'Shall not invoke' col0, 'N' flg, '0' arg FROM dual
6 )
7 );
Table created.
SQL>
SQL> SELECT *
2 FROM base_table t0
3 LEFT JOIN TABLE( test_package.testFunc(t0.arg) ) t1
4 ON (t0.flg = 'Y');
COL0 F A COLUMN_VALUE
---------------- - - ------------
Shall invoke Y 2 1
Shall invoke Y 2 2
Shall not invoke N 0
SQL>
您可以检查explain计划以查看函数是如何调用的以及执行了多少次
SQL> EXPLAIN PLAN FOR
2 SELECT *
3 FROM base_table t0
4 LEFT JOIN TABLE( test_package.testFunc(t0.arg) ) t1
5 ON (t0.flg = 'Y');
Explained.
SQL>
SQL> SELECT * FROM TABLE(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------
Plan hash value: 2726614787
--------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 16336 | 510K| 61 (0)| 00:00:01 |
| 1 | NESTED LOOPS OUTER | | 16336 | 510K| 61 (0)| 00:00:01 |
| 2 | TABLE ACCESS FULL | BASE_TABLE | 2 | 38 | 3 (0)| 00:00:01 |
| 3 | VIEW | VW_LAT_D4FD8C38 | 8168 | 103K| 29 (0)| 00:00:01 |
|* 4 | FILTER | | | | | |
| 5 | COLLECTION ITERATOR PICKLER FETCH| TESTFUNC | 8168 | 16336 | 29 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - filter("T0"."FLG"='Y')
17 rows selected.
SQL>
是的,我能。尽管如此,即使标志为“N”,也会调用该函数。每个函数调用都要花费大量的处理时间准备函数调用堆栈、更改上下文等。这不是我想要的。b我也必须选择“不应调用”行。无论如何,谢谢你的回复。我做了编辑。在这个示例中,您仍然得到Shall not invoke行,并且根据服务器输出,该函数只被调用一次。谢谢,好先生!你可能会笑,但我尝试过条件连接,但与你的解决方案不同的是,我忘记了“左”的说法:那个业余错误花费了我大量的时间、精力和时间。再次,非常感谢。问题是,我必须同时选择“不应调用”和“应调用”行。正如我在上一个清单中所描述的那样,我已经尝试对“N”应用过滤器,然后将其与“Y”合并。这种方法有一个很大的缺点——如果使用union/union all,索引将停止工作
SQL> EXPLAIN PLAN FOR
2 SELECT *
3 FROM base_table t0
4 LEFT JOIN TABLE( test_package.testFunc(t0.arg) ) t1
5 ON (t0.flg = 'Y');
Explained.
SQL>
SQL> SELECT * FROM TABLE(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------------------------------
Plan hash value: 2726614787
--------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 16336 | 510K| 61 (0)| 00:00:01 |
| 1 | NESTED LOOPS OUTER | | 16336 | 510K| 61 (0)| 00:00:01 |
| 2 | TABLE ACCESS FULL | BASE_TABLE | 2 | 38 | 3 (0)| 00:00:01 |
| 3 | VIEW | VW_LAT_D4FD8C38 | 8168 | 103K| 29 (0)| 00:00:01 |
|* 4 | FILTER | | | | | |
| 5 | COLLECTION ITERATOR PICKLER FETCH| TESTFUNC | 8168 | 16336 | 29 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - filter("T0"."FLG"='Y')
17 rows selected.
SQL>