如何在查询中确定基数?

时间:2015-11-09 14:21:46

标签: oracle oracle11g

我有两个返回相同结果集的查询,但在查看执行计划时,它们具有不同的基数值。

查询是:

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更具限制性。

如果我的假设不正确,我真的想知道是什么决定了这个基数。

提前感谢您的任何帮助

3 个答案:

答案 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)