如何在Oracle SQL中有条件地联接表函数(该函数有一个主表行列作为参数),而不需要过多的函数调用?

如何在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

我有以下问题: 我的基表中的每一行在下面的列表中都有一个标志列“flg”和一个函数参数列“arg”。如果N行中的标志为“Y”,则必须调用函数;之后,它将返回,比方说,一列实际上是一堆,但我将尽可能地简化。最后,行N应该转换为子表—函数返回的行N和列的完全联接。 如果标志为“N”,则第N行的结果应为该行本身加上该函数返回列中的NULL

下面是一个例子:

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>