SQL 从 Oracle 转换为 ANSI JOIN
SQL convert from Oracle to ANSI JOIN
如何从旧的 Oracle 连接类型转换为 ANSI 连接类型,为什么?
SELECT *
FROM a, b, c
WHERE b.id (+) = a.id1
AND b.xxx = c.yyy
AND c.id (+) = a.id2
--应该是这个1吧?
select * from
A
left outer join B on B.ID = A.ID1
left outer join C on C.ID = A.ID2 AND B.xxx = C.yyy
--还是这2个?
select * from
A
left outer join C on C.ID = A.ID2
left outer join B on B.ID = A.ID1 AND B.xxx = C.yyy
根据 Oracle documentation:
If the WHERE clause contains a condition that compares a column from table B with a constant, then the (+) operator must be applied to the column so that Oracle returns the rows from table A for which it has generated nulls for this column. Otherwise Oracle returns only the results of a simple join.
因此 b
和 c
之间存在内部连接。并且由于整体条件,这会将所有连接变成 INNER JOIN
(b
和 c
中需要有效值才能使该条件起作用。
我认为等价的逻辑是:
SELECT *
FROM a JOIN
b
ON b.id = a.id1 JOIN
c
ON c.id = a.id2 AND b.xxx = c.yyyy;
也就是说,简单的相等消除将外连接变成内连接。
当然可以测试一下
如果您 运行 简单 explain plan
并检查结果,您可能会看到它生成两个 inner join
s:
explain plan set statement_id = 'test' for
SELECT *
FROM a, b, c
WHERE
b.id (+) = a.id1
AND b.xxx = c.yyy
AND c.id (+) = a.id2
select *
from table(dbms_xplan.display(null, 'test'))
| PLAN_TABLE_OUTPUT |
| :--------------------------------------------------------------------------- |
| Plan hash value: 1502482080 |
| |
| ---------------------------------------------------------------------------- |
| | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | |
| ---------------------------------------------------------------------------- |
| | 0 | SELECT STATEMENT | | 1 | 78 | 6 (0)| 00:00:01 | |
| |* 1 | HASH JOIN | | 1 | 78 | 6 (0)| 00:00:01 | |
| |* 2 | HASH JOIN | | 1 | 52 | 4 (0)| 00:00:01 | |
| | 3 | TABLE ACCESS FULL| A | 1 | 26 | 2 (0)| 00:00:01 | |
| | 4 | TABLE ACCESS FULL| B | 1 | 26 | 2 (0)| 00:00:01 | |
| | 5 | TABLE ACCESS FULL | C | 1 | 26 | 2 (0)| 00:00:01 | |
| ---------------------------------------------------------------------------- |
| |
| Predicate Information (identified by operation id): |
| --------------------------------------------------- |
| |
| 1 - access("B"."XXX"="C"."YYY" AND "C"."ID"="A"."ID2") |
| 2 - access("B"."ID"="A"."ID1") |
| |
| Note |
| ----- |
| - dynamic statistics used: dynamic sampling (level=2) |
explain plan set statement_id = 'test1' for
select *
from a
left join b
on b.id = a.id1
left join c
on c.id = a.id2
and b.xxx = c.yyy
select *
from table(dbms_xplan.display(null, 'test1'))
| PLAN_TABLE_OUTPUT |
| :--------------------------------------------------------------------------- |
| Plan hash value: 2316364204 |
| |
| ---------------------------------------------------------------------------- |
| | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | |
| ---------------------------------------------------------------------------- |
| | 0 | SELECT STATEMENT | | 1 | 78 | 6 (0)| 00:00:01 | |
| |* 1 | HASH JOIN OUTER | | 1 | 78 | 6 (0)| 00:00:01 | |
| |* 2 | HASH JOIN OUTER | | 1 | 52 | 4 (0)| 00:00:01 | |
| | 3 | TABLE ACCESS FULL| A | 1 | 26 | 2 (0)| 00:00:01 | |
| | 4 | TABLE ACCESS FULL| B | 1 | 26 | 2 (0)| 00:00:01 | |
| | 5 | TABLE ACCESS FULL | C | 1 | 26 | 2 (0)| 00:00:01 | |
| ---------------------------------------------------------------------------- |
| |
| Predicate Information (identified by operation id): |
| --------------------------------------------------- |
| |
| 1 - access("C"."ID"(+)="A"."ID2" AND "B"."XXX"="C"."YYY"(+)) |
| 2 - access("B"."ID"(+)="A"."ID1") |
| |
| Note |
| ----- |
| - dynamic statistics used: dynamic sampling (level=2) |
db<>fiddle here
你的选择都没有:
select *
from A
left outer join B on B.ID = A.ID1
left outer join C on C.ID = A.ID2 AND B.xxx = C.yyy
可以写成:
SELECT *
FROM a, b, c
WHERE b.id (+) = a.id1
AND b.xxx = c.yyy (+)
AND c.id (+) = a.id2
和:
select *
from A
left outer join C on C.ID = A.ID2
left outer join B on B.ID = A.ID1 AND B.xxx = C.yyy
可以写成:
SELECT *
FROM a, b, c
WHERE b.id (+) = a.id1
AND b.xxx (+) = c.yyy
AND c.id (+) = a.id2
你拥有的是:
SELECT *
FROM a
INNER JOIN b ON (a.id1 = b.id)
INNER JOIN c ON (a.id2 = c.id AND b.xxx = c.yyy)
why?
SELECT *
FROM a, b, c
WHERE b.id (+) = a.id1
AND b.xxx = c.yyy
AND c.id (+) = a.id2
行:
AND b.xxx = c.yyy
要求有b
行和c
行;当存在左外连接时不会发生这种情况,因此连接等同于内连接并且查询可以重写为:
SELECT *
FROM a, b, c
WHERE b.id = a.id1
AND b.xxx = c.yyy
AND c.id = a.id2
然后所有连接都是内部连接就更清楚了。
您可能打算写的是:
select *
from A,
(
SELECT b.id AS b_id,
c.id AS c_id,
b.xxx,
c.yyy
FROM b, c
WHERE b.xxx = c.yyy
) bc
WHERE bc.b_id (+) = a.id1
AND bc.c_id (+) = a.id2
这将是:
select *
from A
left outer join (
SELECT b.id AS b_id,
c.id AS c_id,
b.xxx,
c.yyy
FROM b
INNER JOIN c ON b.xxx = c.yyy
) bc
on bc.b_id = a.id1 AND bc.c_id = a.id2
db<>fiddle here
您也可以使用 Oracle SQL Developer 作弊。
粘贴您的 ANSI SQL 查询或 Oracle SQL 查询,select 它,右键单击,然后使用转换功能。
我们的 SQL 解析器将为您重写 JOINS。
所以这适用于任何查询,而不仅仅是您在 question/scenario 中的查询。
您当然应该检查查询返回的执行计划和数据,以确保它们在功能上是等效的。
如何从旧的 Oracle 连接类型转换为 ANSI 连接类型,为什么?
SELECT *
FROM a, b, c
WHERE b.id (+) = a.id1
AND b.xxx = c.yyy
AND c.id (+) = a.id2
--应该是这个1吧?
select * from
A
left outer join B on B.ID = A.ID1
left outer join C on C.ID = A.ID2 AND B.xxx = C.yyy
--还是这2个?
select * from
A
left outer join C on C.ID = A.ID2
left outer join B on B.ID = A.ID1 AND B.xxx = C.yyy
根据 Oracle documentation:
If the WHERE clause contains a condition that compares a column from table B with a constant, then the (+) operator must be applied to the column so that Oracle returns the rows from table A for which it has generated nulls for this column. Otherwise Oracle returns only the results of a simple join.
因此 b
和 c
之间存在内部连接。并且由于整体条件,这会将所有连接变成 INNER JOIN
(b
和 c
中需要有效值才能使该条件起作用。
我认为等价的逻辑是:
SELECT *
FROM a JOIN
b
ON b.id = a.id1 JOIN
c
ON c.id = a.id2 AND b.xxx = c.yyyy;
也就是说,简单的相等消除将外连接变成内连接。
当然可以测试一下
如果您 运行 简单 explain plan
并检查结果,您可能会看到它生成两个 inner join
s:
explain plan set statement_id = 'test' for SELECT * FROM a, b, c WHERE b.id (+) = a.id1 AND b.xxx = c.yyy AND c.id (+) = a.id2
select * from table(dbms_xplan.display(null, 'test'))
| PLAN_TABLE_OUTPUT | | :--------------------------------------------------------------------------- | | Plan hash value: 1502482080 | | | | ---------------------------------------------------------------------------- | | | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | | | ---------------------------------------------------------------------------- | | | 0 | SELECT STATEMENT | | 1 | 78 | 6 (0)| 00:00:01 | | | |* 1 | HASH JOIN | | 1 | 78 | 6 (0)| 00:00:01 | | | |* 2 | HASH JOIN | | 1 | 52 | 4 (0)| 00:00:01 | | | | 3 | TABLE ACCESS FULL| A | 1 | 26 | 2 (0)| 00:00:01 | | | | 4 | TABLE ACCESS FULL| B | 1 | 26 | 2 (0)| 00:00:01 | | | | 5 | TABLE ACCESS FULL | C | 1 | 26 | 2 (0)| 00:00:01 | | | ---------------------------------------------------------------------------- | | | | Predicate Information (identified by operation id): | | --------------------------------------------------- | | | | 1 - access("B"."XXX"="C"."YYY" AND "C"."ID"="A"."ID2") | | 2 - access("B"."ID"="A"."ID1") | | | | Note | | ----- | | - dynamic statistics used: dynamic sampling (level=2) |
explain plan set statement_id = 'test1' for select * from a left join b on b.id = a.id1 left join c on c.id = a.id2 and b.xxx = c.yyy
select * from table(dbms_xplan.display(null, 'test1'))
| PLAN_TABLE_OUTPUT | | :--------------------------------------------------------------------------- | | Plan hash value: 2316364204 | | | | ---------------------------------------------------------------------------- | | | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | | | ---------------------------------------------------------------------------- | | | 0 | SELECT STATEMENT | | 1 | 78 | 6 (0)| 00:00:01 | | | |* 1 | HASH JOIN OUTER | | 1 | 78 | 6 (0)| 00:00:01 | | | |* 2 | HASH JOIN OUTER | | 1 | 52 | 4 (0)| 00:00:01 | | | | 3 | TABLE ACCESS FULL| A | 1 | 26 | 2 (0)| 00:00:01 | | | | 4 | TABLE ACCESS FULL| B | 1 | 26 | 2 (0)| 00:00:01 | | | | 5 | TABLE ACCESS FULL | C | 1 | 26 | 2 (0)| 00:00:01 | | | ---------------------------------------------------------------------------- | | | | Predicate Information (identified by operation id): | | --------------------------------------------------- | | | | 1 - access("C"."ID"(+)="A"."ID2" AND "B"."XXX"="C"."YYY"(+)) | | 2 - access("B"."ID"(+)="A"."ID1") | | | | Note | | ----- | | - dynamic statistics used: dynamic sampling (level=2) |
db<>fiddle here
你的选择都没有:
select *
from A
left outer join B on B.ID = A.ID1
left outer join C on C.ID = A.ID2 AND B.xxx = C.yyy
可以写成:
SELECT *
FROM a, b, c
WHERE b.id (+) = a.id1
AND b.xxx = c.yyy (+)
AND c.id (+) = a.id2
和:
select *
from A
left outer join C on C.ID = A.ID2
left outer join B on B.ID = A.ID1 AND B.xxx = C.yyy
可以写成:
SELECT *
FROM a, b, c
WHERE b.id (+) = a.id1
AND b.xxx (+) = c.yyy
AND c.id (+) = a.id2
你拥有的是:
SELECT *
FROM a
INNER JOIN b ON (a.id1 = b.id)
INNER JOIN c ON (a.id2 = c.id AND b.xxx = c.yyy)
why?
SELECT *
FROM a, b, c
WHERE b.id (+) = a.id1
AND b.xxx = c.yyy
AND c.id (+) = a.id2
行:
AND b.xxx = c.yyy
要求有b
行和c
行;当存在左外连接时不会发生这种情况,因此连接等同于内连接并且查询可以重写为:
SELECT *
FROM a, b, c
WHERE b.id = a.id1
AND b.xxx = c.yyy
AND c.id = a.id2
然后所有连接都是内部连接就更清楚了。
您可能打算写的是:
select *
from A,
(
SELECT b.id AS b_id,
c.id AS c_id,
b.xxx,
c.yyy
FROM b, c
WHERE b.xxx = c.yyy
) bc
WHERE bc.b_id (+) = a.id1
AND bc.c_id (+) = a.id2
这将是:
select *
from A
left outer join (
SELECT b.id AS b_id,
c.id AS c_id,
b.xxx,
c.yyy
FROM b
INNER JOIN c ON b.xxx = c.yyy
) bc
on bc.b_id = a.id1 AND bc.c_id = a.id2
db<>fiddle here
您也可以使用 Oracle SQL Developer 作弊。
粘贴您的 ANSI SQL 查询或 Oracle SQL 查询,select 它,右键单击,然后使用转换功能。
我们的 SQL 解析器将为您重写 JOINS。
所以这适用于任何查询,而不仅仅是您在 question/scenario 中的查询。
您当然应该检查查询返回的执行计划和数据,以确保它们在功能上是等效的。