人们会期望 Oracle 应用 transitive closure 的规则并使用您提供的信息来证明多余的谓词是不必要的 (and it often does so, as I've shown in this blog post),但它似乎并没有这样做案例(没有约束或索引)。获取这两个查询的执行计划:
SELECT s.sql_id, p.*
FROM v$sql s, TABLE (
dbms_xplan.display_cursor (
s.sql_id, s.child_number, 'ALLSTATS LAST'
)
) p
WHERE s.sql_text LIKE '%a,b,c,d%'
产量:
SQL_ID d54mttn9psd29, child number 0
-------------------------------------
--Q2
select * from a,b,c,d
where a.id = b.id
and a.id = c.id
and
a.id = d.id
-- Q2 differs from Q1 only in the next 3 lines
and b.id = c.id
and b.id = d.id
and c.id = d.id
Plan hash value: 3564259801
-------------------------------------------------------------------------
| Id | Operation | Name | E-Rows | OMem | 1Mem | Used-Mem |
-------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | | |
|* 1 | HASH JOIN | | 1 | 1068K| 1068K| 240K (0)|
|* 2 | HASH JOIN | | 1 | 1209K| 1209K| 505K (0)|
|* 3 | HASH JOIN | | 1 | 1506K| 1506K| 461K (0)|
| 4 | TABLE ACCESS FULL| A | 1 | | | |
| 5 | TABLE ACCESS FULL| B | 1 | | | |
| 6 | TABLE ACCESS FULL | C | 1 | | | |
| 7 | TABLE ACCESS FULL | D | 1 | | | |
-------------------------------------------------------------------------
SQL_ID 3zzwv0z5tq84f, child number 0
-------------------------------------
--Q1
select * from a,b,c,d
where a.id = b.id
and a.id = c.id
and
a.id = d.id
Plan hash value: 255250992
---------------------------------------------------------------------------
| Id | Operation | Name | E-Rows | OMem | 1Mem | Used-Mem |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | | |
|* 1 | HASH JOIN | | 1 | 1068K| 1068K| 178K (0)|
| 2 | MERGE JOIN CARTESIAN | | 1 | | | |
| 3 | MERGE JOIN CARTESIAN| | 1 | | | |
| 4 | TABLE ACCESS FULL | B | 1 | | | |
| 5 | BUFFER SORT | | 1 | 73728 | 73728 | |
| 6 | TABLE ACCESS FULL | C | 1 | | | |
| 7 | BUFFER SORT | | 1 | 73728 | 73728 | |
| 8 | TABLE ACCESS FULL | D | 1 | | | |
| 9 | TABLE ACCESS FULL | A | 1 | | | |
---------------------------------------------------------------------------
这令人惊讶。在没有对空表计算统计信息的情况下,E-Rows 值是正确的,但内存估计值相差甚远,并且两个查询的内存估计值不同。
这并不意味着一种方法总是比另一种更好甚至不同,但它可能。例如。当我将实际数据添加到表中并计算统计数据时:
INSERT INTO a SELECT LEVEL FROM dual CONNECT BY LEVEL <= 10000;
INSERT INTO b SELECT LEVEL FROM dual CONNECT BY LEVEL <= 10000;
INSERT INTO c SELECT LEVEL FROM dual CONNECT BY LEVEL <= 10000;
INSERT INTO d SELECT LEVEL FROM dual CONNECT BY LEVEL <= 10000;
BEGIN
DBMS_STATS.GATHER_TABLE_STATS('TEST', 'A');
DBMS_STATS.GATHER_TABLE_STATS('TEST', 'B');
DBMS_STATS.GATHER_TABLE_STATS('TEST', 'C');
DBMS_STATS.GATHER_TABLE_STATS('TEST', 'D');
END;
然后,我得到了与哈希连接相同的执行计划,而不是合并连接:
SQL_ID d54mttn9psd29, child number 0
-------------------------------------
--Q2
select * from a,b,c,d
where a.id = b.id
and a.id = c.id
and
a.id = d.id
-- Q2 differs from Q1 only in the next 3 lines
and b.id = c.id
and b.id = d.id
and c.id = d.id
Plan hash value: 2782485219
-------------------------------------------------------------------------
| Id | Operation | Name | E-Rows | OMem | 1Mem | Used-Mem |
-------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | | |
|* 1 | HASH JOIN | | 1 | 1836K| 1836K| 2032K (0)|
|* 2 | HASH JOIN | | 1 | 2161K| 2161K| 2145K (0)|
| 3 | TABLE ACCESS FULL | C | 10000 | | | |
|* 4 | HASH JOIN | | 10000 | 2546K| 2546K| 2211K (0)|
| 5 | TABLE ACCESS FULL| A | 10000 | | | |
| 6 | TABLE ACCESS FULL| B | 10000 | | | |
| 7 | TABLE ACCESS FULL | D | 10000 | | | |
-------------------------------------------------------------------------
SQL_ID 3zzwv0z5tq84f, child number 0
-------------------------------------
--Q1
select * from a,b,c,d
where a.id = b.id
and a.id = c.id
and
a.id = d.id
Plan hash value: 388154631
-------------------------------------------------------------------------
| Id | Operation | Name | E-Rows | OMem | 1Mem | Used-Mem |
-------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | | | |
|* 1 | HASH JOIN | | 10000 | 2546K| 2546K| 2099K (0)|
| 2 | TABLE ACCESS FULL | D | 10000 | | | |
|* 3 | HASH JOIN | | 10000 | 2546K| 2546K| 2146K (0)|
| 4 | TABLE ACCESS FULL | C | 10000 | | | |
|* 5 | HASH JOIN | | 10000 | 2546K| 2546K| 2211K (0)|
| 6 | TABLE ACCESS FULL| A | 10000 | | | |
| 7 | TABLE ACCESS FULL| B | 10000 | | | |
-------------------------------------------------------------------------
这里的学习和往常一样。不要相信你的直觉。不要相信互联网上的任何旧博客文章。查看执行计划并进行衡量。
我为此使用了 Oracle Database 18c Express Edition。