在我们的PL/SQL代码中,我发现了一个错误,当查询的WHERE子句中使用的变量为NULL时。所以我们在查询的 WHERE 子句中有一个语句,比如"table1.column1 = NULL"。在这种情况下,我已经知道对 UNKNOWN 的评估,并且正如预期的那样,查询没有返回任何记录。
但是"table1"很大,我看到长查询将我们错误地空变量到这个表,它跟踪索引。因此,我进行了一些小研究,发现以下查询:
1.
select *
from table1
where table1.column1 = NULL
阿拉伯数字。
select *
from table1
where 1=2
实际执行。它们都具有正确的计划和正常(作为WHERE
子句中具有正确数据的查询(执行时间。此外,此查询会消耗资源。
问题是:为什么完全不会返回任何结果的查询仍然在 Oracle 中执行并消耗资源?
优化程序可能不希望用户编写无意义的查询,因此不会检查它。
请注意,每次检查无意义的查询都会花费额外的时间,并且大多数情况不会从中获得任何好处。
为什么要付出额外的努力来惩罚大量的查询,以简单地避免错误查询的负载?惩罚无用查询的作者似乎是正确的,而不是所有其他的人来取悦他。
优化程序确实意识到了这一点,并将在查询中注入"stop this"过滤器:
我有一个包含 300 万行的表test
。一个简单的
select *
from test;
结果为以下执行计划:
----------------------------------------------------------------
| Id | Operation | Name | E-Rows |E-Bytes| Cost (%CPU)|
----------------------------------------------------------------
| 0 | SELECT STATEMENT | | 3145K| 72M| 3587 (1)|
| 1 | TABLE ACCESS FULL| TEST | 3145K| 72M| 3587 (1)|
----------------------------------------------------------------
现在让我们看一下以下计划:
select *
from test
where 1=2;
-----------------------------------------------------------------
| Id | Operation | Name | E-Rows |E-Bytes| Cost (%CPU)|
-----------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 24 | 0 (0)|
|* 1 | FILTER | | | | |
| 2 | TABLE ACCESS FULL| TEST | 3145K| 72M| 3587 (1)|
-----------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(NULL IS NOT NULL)
正如你所看到的,Oracle确实意识到WHERE条件永远不会为真,并注入了一个计划步骤,该步骤基本上阻止了其下方"表访问完整"的执行。估计行 1
如果检索运行时使用的实际计划,也可以看到:
------------------------------------------------------------------------------------------------
| Id | Operation | Name | Starts | E-Rows |E-Bytes| Cost (%CPU)| A-Rows | A-Time |
------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | | | 1 (100)| 0 |00:00:00.01 |
|* 1 | FILTER | | 1 | | | | 0 |00:00:00.01 |
| 2 | TABLE ACCESS FULL| TEST | 0 | 3145K| 72M| 3587 (1)| 0 |00:00:00.01 |
------------------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
1 - SEL$1
2 - SEL$1 / TEST@SEL$1
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(NULL IS NOT NULL)
您可以看到所有计划步骤(包括"表访问完整"(的"A 行"(="实际行"(为零,这意味着从未从表中读取数据。
请注意,对于不太明显的条件,但可以从约束中推断出永远不会为真的条件也会发生这种情况。 测试表的id
列是主键,因此NOT NULL
运行时:
select *
from test
where id is null;
优化程序知道这是一个永远不会为真的条件(因为 NOT NULL 约束(,并生成与以前相同的"停止此"计划:
-----------------------------------------------------------------
| Id | Operation | Name | E-Rows |E-Bytes| Cost (%CPU)|
-----------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 24 | 0 (0)|
|* 1 | FILTER | | | | |
| 2 | TABLE ACCESS FULL| TEST | 3145K| 72M| 3587 (1)|
-----------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(NULL IS NOT NULL)