我的表A有以下结构
create table A {
id Oracle Execution Plan primary key,
value varchar(20)
}
我有以下查询:
select * from a where id in (123) or id in (select 133 from dual);
我得到了这个解释计划:
为什么我们这里有表访问权限?我的意思是我们正在使用PK进行过滤,因为我们知道PK被编入索引。此外,我将查询更改为
select * from a where id in (123) or id in (55);
我们有以下exp。计划: 因此,对于子查询,我们有表访问权限,而我们没有。为什么?
答案 0 :(得分:2)
如果检查DBMS_Plan的完整输出,您可能会发现第二个查询已转换为:
id in (123,55)
...并且第一个查询不是。
您似乎在表中没有太多数据,并且优化器知道这一点,因此索引和全表访问方法之间的成本差异无论如何都非常小。优化器将考虑它认为id列中的最大值和最小值,以及它们的分布,以确定在选择计划之前结果集的可能基数。
您必须非常小心,以确保您正在处理优化查询所需的真实数据集 - 优化器很难用假日期来愚弄。
答案 1 :(得分:2)
使用子查询我们有表访问权限,而没有它我们没有。
这是因为优化程序将谓词重写作为 OR EXISTS
。您可以在EXPLAIN PLAN中的过滤器中看到相同内容。
id in (123) or id in (select 133 from dual)
与id in (123) or id in (55)
不同。优化器重写谓词中的子查询。
or id in (select 133 from dual)
被重写为 OR EXISTS (SELECT 0 FROM "SYS"."DUAL""DUAL" WHERE :B1=133))
例如,
案例#1
SQL> EXPLAIN PLAN FOR
2 SELECT * FROM emp WHERE empno = 7369 or empno = 7499;
Explained.
SQL>
SQL> SELECT * FROM TABLE(dbms_xplan.display);
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------
Plan hash value: 2355049923
---------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 78 | 2 (0)| 00:00:01 |
| 1 | INLIST ITERATOR | | | | | |
| 2 | TABLE ACCESS BY INDEX ROWID| EMP | 2 | 78 | 2 (0)| 00:00:01 |
|* 3 | INDEX UNIQUE SCAN | PK_EMP | 2 | | 1 (0)| 00:00:01 |
---------------------------------------------------------------------------------------
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("EMPNO"=7369 OR "EMPNO"=7499)
15 rows selected.
案例#2
SQL> EXPLAIN PLAN FOR
2 SELECT * FROM emp WHERE empno = 7369 OR empno IN(SELECT 7499 FROM dual);
Explained.
SQL>
SQL> SELECT * FROM TABLE(dbms_xplan.display);
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------
Plan hash value: 3969125370
---------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 78 | 3 (0)| 00:00:01 |
|* 1 | FILTER | | | | | |
| 2 | TABLE ACCESS FULL| EMP | 14 | 546 | 3 (0)| 00:00:01 |
|* 3 | FILTER | | | | | |
| 4 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 |
---------------------------------------------------------------------------
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("EMPNO"=7369 OR EXISTS (SELECT 0 FROM "SYS"."DUAL"
"DUAL" WHERE :B1=7499))
3 - filter(:B1=7499)
18 rows selected.
因此,您会看到重写查询,以便将过滤器应用为
filter("EMPNO"=7369 OR EXISTS (SELECT 0 FROM "SYS"."DUAL"
"DUAL" WHERE :B1=7499))**
现在,让我们执行重写的查询。修改后:
SQL> EXPLAIN PLAN FOR
2 SELECT * FROM emp
3 WHERE EMPNO =7369 OR EXISTS (SELECT 0 FROM DUAL WHERE :B1=7499);
Explained.
SQL>
SQL> SELECT * FROM TABLE(dbms_xplan.display);
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------
Plan hash value: 2411523692
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 78 | 3 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| EMP | 2 | 78 | 3 (0)| 00:00:01 |
|* 2 | FILTER | | | | | |
| 3 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 |
--------------------------------------------------------------------------
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("EMPNO"=7369 OR EXISTS (SELECT 0 FROM "SYS"."DUAL"
"DUAL" WHERE TO_NUMBER(:B1)=7499))
2 - filter(TO_NUMBER(:B1)=7499)
17 rows selected.
SQL>
因此,您现在可以看到谓词重写和相同的修改后的查询具有类似的解释计划,并执行 FULL TABLE SCAN 。