在遗留代码中,我发现了一些奇怪的SQL SELECT查询,导致我们的应用程序出错。我的Oracle版本 11.2.0.1.0 。下面(简化)代码:
DDL:
SELECT COUNT(*)
FROM
(
SELECT
B.ID,
B.SURNAME,
B.NAME
FROM A
LEFT JOIN B ON B.ID = A.ID_B
LEFT OUTER JOIN (SELECT * FROM C WHERE ID = 10) C ON 1 = 1
WHERE A.ID_B = 10
);
选择:
{{1}}
问题: 你可以帮我理解为什么select count(*)返回0,因为子查询返回1结果行? 当我删除'A_INDEX'时,两个选择都正常(count(*)返回1)。
答案 0 :(得分:2)
如果没有索引,则带有count(*)的查询会执行此执行计划 Linux中的:版本11.2.0.3.0
------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 15 | 5 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 15 | | |
| 2 | MERGE JOIN OUTER | | 1 | 15 | 5 (0)| 00:00:01 |
|* 3 | TABLE ACCESS FULL | A | 1 | 13 | 3 (0)| 00:00:01 |
| 4 | BUFFER SORT | | 1 | 2 | 2 (0)| 00:00:01 |
| 5 | VIEW | | 1 | 2 | 2 (0)| 00:00:01 |
|* 6 | TABLE ACCESS FULL| C | 1 | 13 | 2 (0)| 00:00:01 |
------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter("A"."ID_B"=10)
6 - filter("ID"=10)
请注意,Oracle不访问表B并在A和C上执行外连接, 这导致结果1 - 正确
使用A 上的索引 - Oracle打开CARTESIAN JOIN
导致结果1 * 0 = 0(此联接不是OUTER
)
---------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 15 | 2 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 15 | | |
| 2 | MERGE JOIN CARTESIAN| | 1 | 15 | 2 (0)| 00:00:01 |
| 3 | VIEW | | 1 | 2 | 2 (0)| 00:00:01 |
|* 4 | TABLE ACCESS FULL | C | 1 | 13 | 2 (0)| 00:00:01 |
| 5 | BUFFER SORT | | 1 | 13 | 2 (0)| 00:00:01 |
|* 6 | INDEX RANGE SCAN | A_INDEX | 1 | 13 | 0 (0)| 00:00:01 |
---------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - filter("ID"=10)
6 - access("A"."ID_B"=10)
对于坏消息感到抱歉,IMO在Oracle中这是一个错误的优化,唯一的机会是打开SR(或者使用drop index或重新构造查询来解决它)。
对于completness,使用提示/*+ NO_QUERY_TRANSFORMATION */
的查询计划
现在可以访问表B
并且两个连接都是外部的,因此它可以正常工作。
------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 80 | 4 (0)| 00:00:01 |
| 1 | MERGE JOIN OUTER | | 1 | 80 | 4 (0)| 00:00:01 |
| 2 | MERGE JOIN OUTER | | 1 | 80 | 2 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | A_INDEX | 1 | 13 | 1 (0)| 00:00:01 |
| 4 | BUFFER SORT | | 1 | 67 | 1 (0)| 00:00:01 |
| 5 | TABLE ACCESS BY INDEX ROWID| B | 1 | 67 | 1 (0)| 00:00:01 |
|* 6 | INDEX UNIQUE SCAN | PK_B | 1 | | 0 (0)| 00:00:01 |
| 7 | BUFFER SORT | | 1 | | 3 (0)| 00:00:01 |
| 8 | VIEW | | 1 | | 2 (0)| 00:00:01 |
|* 9 | TABLE ACCESS FULL | C | 1 | 13 | 2 (0)| 00:00:01 |
------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("A"."ID_B"=10)
6 - access("B"."ID"(+)=10)
9 - filter("ID"=10)
答案 1 :(得分:2)
在11.2.0.4中,无论是否有索引,我得到1的计数。有索引的计划:
---------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 13 | 3 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 13 | | |
| 2 | MERGE JOIN OUTER | | 1 | 13 | 3 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | A_INDEX | 1 | 13 | 1 (0)| 00:00:01 |
| 4 | BUFFER SORT | | 1 | | 2 (0)| 00:00:01 |
| 5 | VIEW | | 1 | | 2 (0)| 00:00:01 |
|* 6 | TABLE ACCESS FULL| C | 1 | 13 | 2 (0)| 00:00:01 |
---------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("A"."ID_B"=10)
6 - filter("ID"=10)
没有:
------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 13 | 5 (0)| 00:00:01 |
| 1 | SORT AGGREGATE | | 1 | 13 | | |
| 2 | MERGE JOIN OUTER | | 1 | 13 | 5 (0)| 00:00:01 |
|* 3 | TABLE ACCESS FULL | A | 1 | 13 | 3 (0)| 00:00:01 |
| 4 | BUFFER SORT | | 1 | | 2 (0)| 00:00:01 |
| 5 | VIEW | | 1 | | 2 (0)| 00:00:01 |
|* 6 | TABLE ACCESS FULL| C | 1 | 13 | 2 (0)| 00:00:01 |
------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - filter("A"."ID_B"=10)
6 - filter("ID"=10)
由于这不是11.2.0.3中的行为,因此可能修复了11.2.0.4补丁集(或其中一个CPU;此环境包括2015年10月的CPU,没有其他补丁)。部分基于我们的评论,/*+ NO_QUERY_TRANSFORMATION */
解决它,这看起来类似于错误12638091,但它看起来不应该影响11.2.0.3 - 只有这样才能确保提出服务请求。 / p>