带有 TABLE 构造函数的 Oracle OUTER APPLY 的行为类似于 CROSS APPLY。漏洞?

Oracle OUTER APPLY with TABLE constructor behaves like CROSS APPLY. Bug?

考虑以下设置:

CREATE TYPE list_t AS VARRAY(10) OF NUMBER(10);
/
CREATE TABLE x (
  id NUMBER(10),
  list list_t
);
INSERT INTO x VALUES (0, list_t());
INSERT INTO x VALUES (1, list_t(1));
INSERT INTO x VALUES (2, list_t(1, 2));

我的直觉告诉我以下查询应该产生相同的结果:

-- 1: Using 12c syntax with table constructor
SELECT x.id, x.list, y.column_value
FROM x
OUTER APPLY TABLE (x.list) y;

-- 2: Using 12c syntax with an explicit (+) operator
SELECT x.id, x.list, y.column_value
FROM x
OUTER APPLY TABLE (x.list)(+) y;

-- 3: Using 12c syntax with derived table
SELECT x.id, x.list, y.column_value
FROM x
OUTER APPLY (SELECT * FROM TABLE (x.list)) y;

-- 4: Using 11g syntax with old outer join (+)
SELECT x.id, x.list, y.column_value
FROM x, TABLE(x.list)(+) y;

-- 5: Using 12c standard syntax with LATERAL (works only on derived tables in this case)
SELECT x.id, x.list, y.column_value
FROM x
LEFT JOIN LATERAL (SELECT * FROM TABLE (x.list)) y ON 1 = 1;

但是,他们 return 的结果并不相同。 #1 的结果似乎有误:

结果 1

(表现得像 CROSS APPLY,似乎是错误的)

ID  LIST                COLUMN_VALUE
------------------------------------
1   TEST.LIST_T(1)      1
2   TEST.LIST_T(1, 2)   1
2   TEST.LIST_T(1, 2)   2

结果 2、3、4 和 5

ID  LIST                COLUMN_VALUE
------------------------------------
0   TEST.LIST_T()
1   TEST.LIST_T(1)      1
2   TEST.LIST_T(1, 2)   1
2   TEST.LIST_T(1, 2)   2

使用存储函数的替代方法

为了说明我的困惑,我可以添加一个仅 return 输入列表的存储函数:

CREATE OR REPLACE FUNCTION f (list list_t) RETURN list_t IS
BEGIN
  RETURN list;
END f;
/

然后在原始查询中使用它:

-- 1: Using 12c syntax with table constructor
SELECT x.id, x.list, y.column_value
FROM x
OUTER APPLY TABLE (f (x.list)) y;

我现在得到了 4 行的预期结果:

ID  LIST                COLUMN_VALUE
------------------------------------
0   TEST.LIST_T()
1   TEST.LIST_T(1)      1
2   TEST.LIST_T(1, 2)   1
2   TEST.LIST_T(1, 2)   2

问题

这是 Oracle 12.2.0.1.0 中的错误,还是 OUTER APPLYTABLE 构造函数应该如何工作?

这是Parser的一个bug,在扩展阶段。 OUTER APPLY(与 12.1 中添加的其他一些功能一样)显然是通过转换为优化器在 11g 中知道的语法来实现的。 第一个查询(错误地)转换为 INNER 联接,而第三个查询(正确)转换为 OUTER 联接。

-- select x.id,x.list,y.column_value from x outer apply table (list) y
select "A1"."ID_0"           "ID",
       "A1"."LIST_1"         "LIST",
       "A1"."COLUMN_VALUE_2" "COLUMN_VALUE"
from   (select "A3"               ."ID" "ID_0",
               "A3"."LIST"         "LIST_1",
               "A2"."COLUMN_VALUE" "COLUMN_VALUE_2"
        from   "DEMO"."X" "A3",
               (select value(a4) "COLUMN_VALUE"
                from   table("A3"."LIST") "A4") "A2"
        where  1 = 1) "A1"

-- select x.id,x.list,y.column_value from x outer apply (select * from table (list)) y
select "A1"."ID_0"           "ID",
       "A1"."LIST_1"         "LIST",
       "A1"."COLUMN_VALUE_2" "COLUMN_VALUE"
from   (select "A3"                 ."ID" "ID_0",
               "A3"."LIST"           "LIST_1",
               "A2"."COLUMN_VALUE_0" "COLUMN_VALUE_2"
        from   "DEMO"."X" "A3",
               lateral((select "A4"."COLUMN_VALUE_0" "COLUMN_VALUE_0"
                       from   lateral((select "A5"."COLUMN_VALUE" "COLUMN_VALUE_0"
                                      from   (select value(a6) "COLUMN_VALUE"
                                              from   table("A3"."LIST") "A6") "A5")) "A4"
                       where  1 = 1))(+) "A2") "A1"