带有 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 APPLY
和 TABLE
构造函数应该如何工作?
这是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"
考虑以下设置:
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 APPLY
和 TABLE
构造函数应该如何工作?
这是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"