如何有条件地加入 Oracle SQL 中的 table 函数,它有一个主要的 table 行列作为参数,而不需要过多的函数调用?

How to conditionally join a table function in Oracle SQL, which has a primary table row column as an argument, without excessive function calls?

我有以下问题: 我的基础 table 中的每一行都有一个标志列(下面列表中的 'flg')和一个函数参数列('arg')。如果在#N 行中标志是 'Y',则必须调用函数;之后,它将 return,比方说,一个专栏(实际上是一堆,但我会尽可能地简化)。最后,行 #N 应该转换为子 table - 行 #N 和列的完全连接,return 由函数编辑。 如果标志为 'N',则第 #N 行的结果应为该行本身加上 "function return column".

中的 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;

它将return我真正想要的:

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';

但随后所有索引都变得无用 - 每个 'order by' 指令都需要很多时间才能完成。

请帮助我实现所需的函数调用行为,并仍然保存原始行顺序。

有什么不明白的随时问我。

此致, 阿列克谢

对于 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 列上创建一个 索引

SQL> set serveroutput on
SQL> with base_table as
  2  (
  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  SELECT * FROM base_table t0, TABLE(test_package.testFunc(t0.arg)) t1
  8  where flg='Y';

COL0             F A COLUMN_VALUE
---------------- - - ------------
Shall invoke     Y 2            1
Shall invoke     Y 2            2

Function call, arg = 2
SQL>

编辑

要在输出中也包含 'flg='N' 的行,您可以执行 OUTER JOIN,不需要 UNION.

例如,

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>

您可以查看解释计划以了解该函数是如何被调用的以及执行了多少次。

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>