一次ORACLE SQL谓词跨界导致的执行计划不准
首先说明谓词跨界一般出现在日期类型中,打个比方你的统计数据是8月20号的,但是今天是8月28日,在这20号到28号之间日期是没有进入统计数据的,
这样可能导致,根据统计信息计算出来的COST异常的小,这样可能导致本来该走其他字段索引的语句走到时间索引上去,导致执行计划最终错误。
在10053中可以看到如下提示:
as selectvity of out-of-range/non-existent value pred
以前多次遇到过,今天再次遇到,就记录于此,如下SQL
select *
from test
where seller_user_id = '123'
and raw_add_time >= trunc(sysdate-1, 'mi')
and raw_add_time < trunc(sysdate, 'mi')
其执行计算如下:
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 2023568823
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 574 |
|* 1 | FILTER | | | |
|* 2 | TABLE ACCESS BY INDEX ROWID| TEST | 1 | 574 |
|* 3 | INDEX RANGE SCAN | TEST_ADD_TIME_INDEX | 2 | |
--------------------------------------------------------------------------------
这里test_ADD_TIME_INDEX就是我的时间上的索引,通过查看选择率后发现seller_user_id远远
小于raw_add_time范围的选择率,应该走到seller_user_id上的索引才对,这里可能发生了
谓词跨界查看其统计数据发现是7月20号的,今天是8月28日。然后进行了一下验证
调整时间后
select *
from test
where seller_user_id = '123'
and raw_add_time >= trunc(sysdate-100, 'mi')
and raw_add_time < trunc(sysdate-99, 'mi')
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 576270446
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cos
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 574 |
|* 1 | FILTER | | | |
|* 2 | TABLE ACCESS BY INDEX ROWID| TEST_BASE_INFO | 1 | 574 |
|* 3 | INDEX RANGE SCAN | TEST_ID_INDEX | 23 | |
--------------------------------------------------------------------------------
这里更改了时间范围在已知的统计数据范围内,执行计划正确。
我们进行10053 TRACE验证:
SINGLE TABLE ACCESS PATH
Single Table Cardinality Estimation for TEST[TEST]
Column (#14):
NewDensity:0.000001, OldDensity:0.000623 BktCnt:254, PopBktCnt:79, PopValCnt:7, NDV:814912
Column (#14): SELLER_USER_ID(
AvgLen: 21 NDV: 814912 Nulls: 855565 Density: 0.000001
Histogram: HtBal #Bkts: 254 UncompBkts: 254 EndPtVals: 183
Column (#41): RAW_ADD_TIME(
AvgLen: 8 NDV: 13533184 Nulls: 0 Density: 0.000000 Min: 2454768 Max: 2457225
Using prorated density: 0.000000 of col #41 as selectvity of out-of-range/non-existent value pred //谓词跨界
Table: TEST Alias: TEST
Card: Original: 27510606.000000 Rounded: 1 Computed: 0.00 Non Adjusted: 0.00
Access Path: TableScan
Cost: 618899.27 Resp: 618899.27 Degree: 0
Cost_io: 616819.00 Cost_cpu: 43729541941
Resp_io: 616819.00 Resp_cpu: 43729541941
Using prorated density: 0.000000 of col #41 as selectvity of out-of-range/non-existent value pred //谓词跨界
Access Path: index (RangeScan)
Index: TEST_ADD_TIME_INDEX
resc_io: 5.00 resc_cpu: 39819
ix_sel: 0.000000 ix_sel_with_filters: 0.000000
Cost: 5.00 Resp: 5.00 Degree: 1
谓词跨界后选择率成为了0.000000,这样导致了执行计划错误。
解决也很简单,按需求重新收集统计信息即可
首先说明谓词跨界一般出现在日期类型中,打个比方你的统计数据是8月20号的,但是今天是8月28日,在这20号到28号之间日期是没有进入统计数据的,
这样可能导致,根据统计信息计算出来的COST异常的小,这样可能导致本来该走其他字段索引的语句走到时间索引上去,导致执行计划最终错误。
在10053中可以看到如下提示:
as selectvity of out-of-range/non-existent value pred
以前多次遇到过,今天再次遇到,就记录于此,如下SQL
select *
from test
where seller_user_id = '123'
and raw_add_time >= trunc(sysdate-1, 'mi')
and raw_add_time < trunc(sysdate, 'mi')
其执行计算如下:
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 2023568823
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 574 |
|* 1 | FILTER | | | |
|* 2 | TABLE ACCESS BY INDEX ROWID| TEST | 1 | 574 |
|* 3 | INDEX RANGE SCAN | TEST_ADD_TIME_INDEX | 2 | |
--------------------------------------------------------------------------------
这里test_ADD_TIME_INDEX就是我的时间上的索引,通过查看选择率后发现seller_user_id远远
小于raw_add_time范围的选择率,应该走到seller_user_id上的索引才对,这里可能发生了
谓词跨界查看其统计数据发现是7月20号的,今天是8月28日。然后进行了一下验证
调整时间后
select *
from test
where seller_user_id = '123'
and raw_add_time >= trunc(sysdate-100, 'mi')
and raw_add_time < trunc(sysdate-99, 'mi')
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 576270446
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cos
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 574 |
|* 1 | FILTER | | | |
|* 2 | TABLE ACCESS BY INDEX ROWID| TEST_BASE_INFO | 1 | 574 |
|* 3 | INDEX RANGE SCAN | TEST_ID_INDEX | 23 | |
--------------------------------------------------------------------------------
这里更改了时间范围在已知的统计数据范围内,执行计划正确。
我们进行10053 TRACE验证:
SINGLE TABLE ACCESS PATH
Single Table Cardinality Estimation for TEST[TEST]
Column (#14):
NewDensity:0.000001, OldDensity:0.000623 BktCnt:254, PopBktCnt:79, PopValCnt:7, NDV:814912
Column (#14): SELLER_USER_ID(
AvgLen: 21 NDV: 814912 Nulls: 855565 Density: 0.000001
Histogram: HtBal #Bkts: 254 UncompBkts: 254 EndPtVals: 183
Column (#41): RAW_ADD_TIME(
AvgLen: 8 NDV: 13533184 Nulls: 0 Density: 0.000000 Min: 2454768 Max: 2457225
Using prorated density: 0.000000 of col #41 as selectvity of out-of-range/non-existent value pred //谓词跨界
Table: TEST Alias: TEST
Card: Original: 27510606.000000 Rounded: 1 Computed: 0.00 Non Adjusted: 0.00
Access Path: TableScan
Cost: 618899.27 Resp: 618899.27 Degree: 0
Cost_io: 616819.00 Cost_cpu: 43729541941
Resp_io: 616819.00 Resp_cpu: 43729541941
Using prorated density: 0.000000 of col #41 as selectvity of out-of-range/non-existent value pred //谓词跨界
Access Path: index (RangeScan)
Index: TEST_ADD_TIME_INDEX
resc_io: 5.00 resc_cpu: 39819
ix_sel: 0.000000 ix_sel_with_filters: 0.000000
Cost: 5.00 Resp: 5.00 Degree: 1
谓词跨界后选择率成为了0.000000,这样导致了执行计划错误。
解决也很简单,按需求重新收集统计信息即可