我有两个返回相同结果集的查询,但在查看执行计划时,它们具有不同的基数值。
查询是:
select acq_cod
, prp
, df_val
, descr
from acqdefprp
where (prp like '%pswd%' or prp like '%Pswd%')
and prp not like '%kno%'
and prp not like '%encr%';
和
select acq_cod
, prp
, df_val
, descr
from acqdefprp
where regexp_instr(prp, 'pswd', 1,1,0,'i' ) > 0
and regexp_instr(prp, '(encr)|(kno)', 1,1,0,'i' ) = 0;
第一个查询有以下解释计划:
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 65 | 4485 | 6 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| acqdefprp | 65 | 4485 | 6 (0)| 00:00:01 |
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(("PRP" LIKE '%pswd%' OR "PRP" LIKE '%Pswd%')
AND "PRP" NOT LIKE '%kno%'
AND "PRP" NOT LIKE '%encr%')
第二个查询的解释计划是:
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 69 | 6 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| acqdefprp | 1 | 69 | 6 (0)| 00:00:01 |
--------------------------------------------------------------------------------
1 - filter(REGEXP_INSTR ("PRP",'(encr)|(kno)',1,1,0,'i') = 0
AND REGEXP_INSTR ("PRP",'pswd',1,1,0,'i') > 0 )
我的问题是为什么两个执行计划之间的基数不同?对于第一个计划,基数(行)是65,而第二个计划是1?
我的假设是这个基数是每个条件返回的最大行数,如果每个条件都单独计算,并且所有这些基于表统计信息。这就是为什么我的第一个查询假定最大值为65,因为WHERE
条件更宽松一些。
这就是为什么对于第二个查询,基数为1,因为regexp_instr
更具限制性。
如果我的假设不正确,我真的想知道是什么决定了这个基数。
提前感谢您的任何帮助
答案 0 :(得分:4)
在您的情况下,表达式太复杂,优化程序无法使用基本统计信息来估计基数。在这些情况下(似乎您没有使用可能影响LIKE
谓词的直方图),使用了固定的选择性:
平等运算符:1% 不等运算符:5%
所以你的
LIKE
示例大约是总表行的(5 % + 5 % - (5 % * 5 %)) * 95 % * 95 % => 8.8 %
。由于- (5 % * 5 %)
运算符,OR
是交集。
REGEX
示例是总表行的1 % * 5 % => 0.05 %
。
Oracle还支持扩展统计信息,您可以在其中计算特定表达式或相关列的统计信息和直方图。
答案 1 :(得分:2)
您可以使用直接WHERE
条件和REGEXP_INSTR
函数来制定计划。实际上使用哪个函数没有区别,因为oracle很难在没有函数执行的情况下给出真正的估计。
例如我们可以创建函数 -
CREATE OR REPLACE FUNCTION f_check(str IN VARCHAR2)
RETURN NUMBER IS
BEGIN
IF str LIKE 'A%' THEN
RETURN 1;
END IF;
RETURN -1;
END;
/
首先选择 -
SELECT *
FROM tmptxt
WHERE dsc LIKE 'A%'
Plan hash value: 2928917536
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 121 | 4356 | 4 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| TMPTXT | 121 | 4356 | 4 (0)| 00:00:01 |
----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("DSC" LIKE 'A%')
和功能 -
SELECT *
FROM tmptxt
WHERE f_check(dsc) = 1
Plan hash value: 2928917536
----------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 36 | 4 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| TMPTXT | 1 | 36 | 4 (0)| 00:00:01 |
----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("F_CHECK"("DSC")=1)
这两个查询给出相同的结果,但计划估计有一些差异。它并不太重要(第一种方式是完全扫描,第二种方式是全扫描),只需要评估整个计划,没有详述数字。
答案 2 :(得分:2)
我的假设是这个基数是每个条件返回的最大行数....
不,基数是估算CBO在操作中将返回多少行。 (技术上总是> = 1)。
基数是根据存储在数据字典中的对象统计数据或动态采样(详情here)计算的。
动态采样成本更高(因为它们是在每个解析中计算的),但可以返回非常精确的结果。
因此,一个可能的变通方法来获得更好的估算是使用动态采样。这里是10级的小型演示( extrem和仅演示,因为整个表在解析步骤中被扫描;但是779行表没有问题, cardinatlity是精确的强>)
create table tst as
select ltrim(to_char(rownum,'09999')) prp from dual connect by level <= 999999;
select count(*) from tst where prp like '%999%';
280
select count(*) from tst where regexp_instr(prp, '999', 1,1,0,'i' ) > 0;
280
Alter session set optimizer_dynamic_sampling=10;
EXPLAIN PLAN SET STATEMENT_ID = 'jara1' into plan_table FOR
select * from tst where prp like '%999%';
SELECT * FROM table(DBMS_XPLAN.DISPLAY('plan_table', 'jara1','ALL'));
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 280 | 1400 | 467 (2)| 00:00:06 |
|* 1 | TABLE ACCESS FULL| TST | 280 | 1400 | 467 (2)| 00:00:06 |
--------------------------------------------------------------------------
1 - filter("PRP" IS NOT NULL AND "PRP" LIKE '%999%')
EXPLAIN PLAN SET STATEMENT_ID = 'jara1' into plan_table FOR
select * from tst where regexp_instr(prp, '999', 1,1,0,'i' ) > 0;
SELECT * FROM table(DBMS_XPLAN.DISPLAY('plan_table', 'jara1','ALL'));
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 280 | 1400 | 479 (5)| 00:00:06 |
|* 1 | TABLE ACCESS FULL| TST | 280 | 1400 | 479 (5)| 00:00:06 |
--------------------------------------------------------------------------
1 - filter( REGEXP_INSTR ("PRP",'999',1,1,0,'i')>0)