越界值导致执行计划走错
最近客户生产上遇到一个统计信息陈旧涉及的 越界 值查询 导致 执行 计划 走错的案例: SQL查询 bankdate = '2013/03/19' 就走到了不合适的索引 IDX_DSF_BANKAPPLHISTORY_02. 下面的 执行 计划 可以忽略DSF_BANKCODE这块。 下面的 执行 计划 可以忽略DSF_BANK
最近客户生产上遇到一个统计信息陈旧涉及的越界值查询导致执行计划走错的案例:
SQL查询bankdate >= '2013/03/19' 就走到了不合适的索引IDX_DSF_BANKAPPLHISTORY_02.
下面的执行计划可以忽略DSF_BANKCODE这块。
下面的执行计划可以忽略DSF_BANKCODE这块。
SQL> select *
2 from dsf_bankapplhistory t
3 where (((((bankdate >= '2013/03/19' and
4 recordnum = 'P00Y0ABFG1E220120204358') and monthnm = '16') and
5 bankcode in
6 (select bankcode
7 from dsf_bankcode
8 where (bankcode = '800000000000' or
9 get_bankcode = '800000000000'))) and flag = '2') and
10 opertype = '1')
11 /
Execution Plan
----------------------------------------------------------
Plan hash value: 3875365240
-----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 631 | 6 (0)| 00:00:01 |
| 1 | NESTED LOOPS SEMI | | 1 | 631 | 6 (0)| 00:00:01 |
|* 2 | TABLE ACCESS BY INDEX ROWID| DSF_BANKAPPLHISTORY | 1 | 605 | 4 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | IDX_DSF_BANKAPPLHISTORY_02 | 1 | | 3 (0)| 00:00:01 |
|* 4 | TABLE ACCESS BY INDEX ROWID| DSF_BANKCODE | 2 | 52 | 2 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | IDX_BANKCODE_1 | 1 | | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("FLAG"='2' AND "RECORDNUM"='P00Y0ABFG1E220120204358' AND "MONTHNM"='16' AND
"OPERTYPE"='1')
3 - access("BANKDATE">='2013/03/19')
4 - filter("GET_BANKCODE"='800000000000' OR "BANKCODE"='800000000000')
5 - access("BANKCODE"="BANKCODE")
生产上的这条SQL语句走错了索引,本身应该走到索引IDX_DSF_BANKAPPLHISTORY_10的,缺走了IDX_DSF_BANKAPPLHISTORY_02。
?2个索引的情况如下:
?
? IDX_DSF_BANKAPPLHISTORY_10(RECORDNUM,MONTHNM??)?
? IDX_DSF_BANKAPPLHISTORY_02(BANKDATE)?
如果查询采用bankdate >= '2013/03/18' 就能走到正确的索引。
SQL> select *
2 from dsf_bankapplhistory t
3 where (((((bankdate >= '2013/03/18' and
4 recordnum = 'P00Y0ABFG1E220120204358') and monthnm = '16') and
5 bankcode in
6 (select bankcode
7 from dsf_bankcode
8 where (bankcode = '800000000000' or
9 get_bankcode = '800000000000'))) and flag = '2') and
10 opertype = '1')
11 /
Execution Plan
----------------------------------------------------------
Plan hash value: 1807757810
-----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 631 | 7 (0)| 00:00:01 |
| 1 | NESTED LOOPS SEMI | | 1 | 631 | 7 (0)| 00:00:01 |
|* 2 | TABLE ACCESS BY INDEX ROWID| DSF_BANKAPPLHISTORY | 1 | 605 | 5 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | IDX_DSF_BANKAPPLHISTORY_10 | 1 | | 4 (0)| 00:00:01 |
|* 4 | TABLE ACCESS BY INDEX ROWID| DSF_BANKCODE | 2 | 52 | 2 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | IDX_BANKCODE_1 | 1 | | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("FLAG"='2' AND "BANKDATE">='2013/03/18' AND "OPERTYPE"='1')
3 - access("RECORDNUM"='P00Y0ABFG1E220120204358' AND "MONTHNM"='16')
4 - filter("GET_BANKCODE"='800000000000' OR "BANKCODE"='800000000000')
5 - access("BANKCODE"="BANKCODE")
采用3月18日之前的日期都能走到正确的索引,而采用3月19日及其之后日期都会走到错误的IDX_DSF_BANKAPPLHISTORY_02索引上。
其实3月18日和3月19日走索引idx_dsf_bankapplhistory_10的成本都没有变化。
SQL> select /*+index(t,idx_dsf_bankapplhistory_10)*/*
2 from dsf_bankapplhistory t
3 where (((((bankdate >= '2013/03/18' and
4 recordnum = 'P00Y0ABFG1E220120204358') and monthnm = '16') and
5 bankcode in
6 (select bankcode
7 from dsf_bankcode
8 where (bankcode = '800000000000' or
9 get_bankcode = '800000000000'))) and flag = '2') and
10 opertype = '1');
Execution Plan
----------------------------------------------------------
Plan hash value: 1807757810
-----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 631 | 7 (0)| 00:00:01 |
| 1 | NESTED LOOPS SEMI | | 1 | 631 | 7 (0)| 00:00:01 |
|* 2 | TABLE ACCESS BY INDEX ROWID| DSF_BANKAPPLHISTORY | 1 | 605 | 5 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | IDX_DSF_BANKAPPLHISTORY_10 | 1 | | 4 (0)| 00:00:01 |
|* 4 | TABLE ACCESS BY INDEX ROWID| DSF_BANKCODE | 2 | 52 | 2 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | IDX_BANKCODE_1 | 1 | | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("FLAG"='2' AND "BANKDATE">='2013/03/18' AND "OPERTYPE"='1')
3 - access("RECORDNUM"='P00Y0ABFG1E220120204358' AND "MONTHNM"='16')
4 - filter("GET_BANKCODE"='800000000000' OR "BANKCODE"='800000000000')
5 - access("BANKCODE"="BANKCODE")
SQL> select /*+index(t,idx_dsf_bankapplhistory_10)*/*
2 from dsf_bankapplhistory t
3 where (((((bankdate >= '2013/03/19' and
4 recordnum = 'P00Y0ABFG1E220120204358') and monthnm = '16') and
5 bankcode in
6 (select bankcode
7 from dsf_bankcode
8 where (bankcode = '800000000000' or
9 get_bankcode = '800000000000'))) and flag = '2') and
10 opertype = '1')
11 /
Execution Plan
----------------------------------------------------------
Plan hash value: 1807757810
-----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 631 | 7 (0)| 00:00:01 |
| 1 | NESTED LOOPS SEMI | | 1 | 631 | 7 (0)| 00:00:01 |
|* 2 | TABLE ACCESS BY INDEX ROWID| DSF_BANKAPPLHISTORY | 1 | 605 | 5 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | IDX_DSF_BANKAPPLHISTORY_10 | 1 | | 4 (0)| 00:00:01 |
|* 4 | TABLE ACCESS BY INDEX ROWID| DSF_BANKCODE | 2 | 52 | 2 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | IDX_BANKCODE_1 | 1 | | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("FLAG"='2' AND "BANKDATE">='2013/03/19' AND "OPERTYPE"='1')
3 - access("RECORDNUM"='P00Y0ABFG1E220120204358' AND "MONTHNM"='16')
4 - filter("GET_BANKCODE"='800000000000' OR "BANKCODE"='800000000000')
5 - access("BANKCODE"="BANKCODE")
而3月18日和3月19日走索引idx_dsf_bankapplhistory_02的成本却降低了1,而估算出的基数也降为1,导致CBO选择了这个错误的索引。
SQL> select /*+index(t,idx_dsf_bankapplhistory_02)*/*
2 from dsf_bankapplhistory t
3 where (((((bankdate >= '2013/03/18' and
4 recordnum = 'P00Y0ABFG1E220120204358') and monthnm = '16') and
5 bankcode in
6 (select bankcode
7 from dsf_bankcode
8 where (bankcode = '800000000000' or
9 get_bankcode = '800000000000'))) and flag = '2') and
10 opertype = '1')
11 /
Execution Plan
----------------------------------------------------------
Plan hash value: 3875365240
-----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 631 | 7 (0)| 00:00:01 |
| 1 | NESTED LOOPS SEMI | | 1 | 631 | 7 (0)| 00:00:01 |
|* 2 | TABLE ACCESS BY INDEX ROWID| DSF_BANKAPPLHISTORY | 1 | 605 | 5 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | IDX_DSF_BANKAPPLHISTORY_02 | 21 | | 3 (0)| 00:00:01 |
|* 4 | TABLE ACCESS BY INDEX ROWID| DSF_BANKCODE | 2 | 52 | 2 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | IDX_BANKCODE_1 | 1 | | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("FLAG"='2' AND "RECORDNUM"='P00Y0ABFG1E220120204358' AND "MONTHNM"='16' AND
"OPERTYPE"='1')
3 - access("BANKDATE">='2013/03/18')
4 - filter("GET_BANKCODE"='800000000000' OR "BANKCODE"='800000000000')
5 - access("BANKCODE"="BANKCODE")
SQL> select /*+index(t,idx_dsf_bankapplhistory_02)*/*
2 from dsf_bankapplhistory t
3 where (((((bankdate >= '2013/03/19' and
4 recordnum = 'P00Y0ABFG1E220120204358') and monthnm = '16') and
5 bankcode in
6 (select bankcode
7 from dsf_bankcode
8 where (bankcode = '800000000000' or
9 get_bankcode = '800000000000'))) and flag = '2') and
10 opertype = '1')
11 /
Execution Plan
----------------------------------------------------------
Plan hash value: 3875365240
-----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 631 | 6 (0)| 00:00:01 |
| 1 | NESTED LOOPS SEMI | | 1 | 631 | 6 (0)| 00:00:01 |
|* 2 | TABLE ACCESS BY INDEX ROWID| DSF_BANKAPPLHISTORY | 1 | 605 | 4 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | IDX_DSF_BANKAPPLHISTORY_02 | 1 | | 3 (0)| 00:00:01 |
|* 4 | TABLE ACCESS BY INDEX ROWID| DSF_BANKCODE | 2 | 52 | 2 (0)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | IDX_BANKCODE_1 | 1 | | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("FLAG"='2' AND "RECORDNUM"='P00Y0ABFG1E220120204358' AND "MONTHNM"='16' AND
"OPERTYPE"='1')
3 - access("BANKDATE">='2013/03/19')
4 - filter("GET_BANKCODE"='800000000000' OR "BANKCODE"='800000000000')
5 - access("BANKCODE"="BANKCODE")
3月18日估算出的基数为21,而3月19日估算出的基数为1(实际上这里估算出的基数应该为0,但一般CBO不允许CARDINALITY为0)。
经查询统计信息过于陈旧:
SQL> select table_name,last_analyzed from dba_tables where table_name='DSF_BANKAPPLHISTORY';
TABLE_NAME LAST_ANALYZED
-------------------- -------------------
DSF_BANKAPPLHISTORY 2012-11-25 21:17:26
这很容易让人联想到越界值的查询,对于越界值的查询将会导致选择性的线性降低,如下图所示:
下面的查询可以查询到BANKDATE的最大和最小值。
SQL> declare
2 v_high_date date;
3 v_low_date date;
4 v_high_value dba_tab_col_statistics.high_value%type;
5 v_low_value dba_tab_col_statistics.low_value%type;
6 begin
7 select high_value,low_value into v_high_value,v_low_value
8 from dba_tab_col_statistics
9 where table_name='DSF_BANKAPPLHISTORY' and column_name='BANKDATE';
10 dbms_stats.convert_raw_value(v_high_value,v_high_date);
11 dbms_stats.convert_raw_value(v_low_value,v_low_date);
12 dbms_output.put_line('high date:'||to_char(v_high_date,'YYYY-MM-DD HH24:MI:SS'));
13 dbms_output.put_line('low date:'||to_char(v_low_date,'YYYY-MM-DD HH24:MI:SS'));
14 end;
15 /
high date:2012-11-25 00:00:00
low date:2012-08-03 00:00:00
PL/SQL procedure successfully completed.
为什么3月19日是转折点,下面的查询可以解析这一点:
SQL> select date '2012-11-25'+(date '2012-11-25' - date '2012-08-03') from dual;
DATE'2012-11-25'+11
-------------------
2013-03-19 00:00:00
其实还有一个转折点就是2012-04-11
SQL> select date '2012-08-03'-(date '2012-11-25' - date '2012-08-03') from dual;
DATE'2012-08-03'-(D
-------------------
2012-04-11 00:00:00
下面我们来看看2012-04-11这个查询的执行计划。
SQL> select *
2 from dsf_bankapplhistory t
3 where bankdate
4 recordnum = 'P00Y0ABFG1E220120204358' and monthnm = '16'
5 /
Execution Plan
----------------------------------------------------------
Plan hash value: 471247677
----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 605 | 4 (0)| 00:00:01 |
|* 1 | TABLE ACCESS BY INDEX ROWID| DSF_BANKAPPLHISTORY | 1 | 605 | 4 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IDX_DSF_BANKAPPLHISTORY_02 | 1 | | 3 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("RECORDNUM"='P00Y0ABFG1E220120204358' AND "MONTHNM"='16')
2 - access("BANKDATE"
SQL> select *
2 from dsf_bankapplhistory t
3 where bankdate
4 recordnum = 'P00Y0ABFG1E220120204358' and monthnm = '16'
5 /
Execution Plan
----------------------------------------------------------
Plan hash value: 477419360
----------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 605 | 5 (0)| 00:00:01 |
|* 1 | TABLE ACCESS BY INDEX ROWID| DSF_BANKAPPLHISTORY | 1 | 605 | 5 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | IDX_DSF_BANKAPPLHISTORY_10 | 1 | | 4 (0)| 00:00:01 |
----------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("BANKDATE"
2 - access("RECORDNUM"='P00Y0ABFG1E220120204358' AND "MONTHNM"='16')

热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

记事本++7.3.1
好用且免费的代码编辑器

SublimeText3汉化版
中文版,非常好用

禅工作室 13.0.1
功能强大的PHP集成开发环境

Dreamweaver CS6
视觉化网页开发工具

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

热门话题

自动化和任务调度在简化软件开发中的重复任务方面发挥着至关重要的作用。想象一下,有一个Python脚本需要每5分钟执行一次,例如从API获取数据、执行数据处理或发送定期更新。如此频繁地手动运行脚本可能非常耗时并且容易出错。这就是任务调度的用武之地。在这篇博文中,我们将探讨如何安排Python脚本每5分钟执行一次,确保它自动运行而无需手动干预。我们将讨论可用于实现此目标的不同方法和库,使您能够有效地自动化任务。使用time.sleep()函数每5分钟运行一次Python脚本的一种简单方法是利用tim

如何使用Python在Linux中进行脚本编写和执行在Linux操作系统中,我们可以使用Python编写并执行各种脚本。Python是一种简洁而强大的编程语言,它提供了丰富的库和工具,使得脚本编写变得更加简单和高效。下面我们将介绍在Linux中如何使用Python进行脚本编写和执行的基本步骤,同时提供一些具体的代码示例来帮助你更好地理解和运用。安装Pytho

中关村消息:4月18日早上,华为突然宣布P70系列手机开启先锋计划正式开售,想要购买的朋友要准备行动起来了,按照以往惯例,华为的旗舰手机非常抢手,会一直处于缺货状态。这次华为P70系列改名为Pura,意为纯粹。在此前华为余承东表示:自2012年起,华为P系列智能手机便如同忠实实的伙伴,伴随全球亿万用户度过了无数珍贵时刻,共同见证了生活中的美好与精彩纷呈。他深刻感悟,每一位选择华为P系列的用户所给予的信任与热爱,无异于一股强大的推动力,始终鼓舞着华为在创新之路上坚定前行。Pura的意思是纯粹的。

win11预览体验计划退出不了?我们在使用win11系统的时候,电脑中都会推出win11预览体验计划供我们使用,但是有小伙伴不想使用这个预览体验计划,希望可以推出这个预览体验计划,如果你不知道应该如何退出,小编下面整理了退出Win11预览体验计划教程攻略,如果你感兴趣的话,小编下面一起往下看看吧!退出Win11预览体验计划教程攻略1、首先按下快捷键“win+i”进入windows设置点击“更新和安全”。2、然后点击左侧任务栏中的“windows预览体验计划”,如图所示。3、此时可以看到右侧的体验

Brown-Forsythe检验是一种统计检验,用于确定两个或多个组的方差是否相等。Levene检验使用与均值的绝对偏差,而Brown-Forsythe检验则使用与中位数的偏差。检验中使用的原假设如下-H0:组(总体)的方差相等备择假设是方差不相等-H1:组(群体)的方差不相等为了执行测试,我们计算每组的中位数以及与中位数的绝对偏差。然后我们根据这些偏差的方差计算F统计量。假设计算出的F统计量大于F分布表中的临界值。在这种情况下,我们拒绝原假设并得出结论:各组的方差不相等。在Python中,sc

Microsoft今天宣布了Dynamics365客户服务中SharePoint与Copilot集成的早期预览版。这种集成将使客户服务代理能够访问更广泛的知识来源,从而提高生产力并改善客户互动。目前,Dynamics365客户服务中的Copilot利用内部知识库为客户服务代理提供指导。通过建议聊天和草稿电子邮件内容,Copilot已成为提高客户服务团队生产力的关键工具。但是,客户反馈表明该工具需要利用来自外部资源(如SharePoint)的知识。SharePoint协同驾驶集成为了响应这些反馈,

怎样在浏览器中编写PHP代码并保持代码不被执行?随着互联网的普及,越来越多的人开始接触网页开发,其中对于PHP的学习也越来越受到关注。PHP是一种在服务器端运行的脚本语言,通常用于编写动态网页。然而,在练习阶段,我们希望能够在浏览器中编写PHP代码并查看结果,但又不希望代码被执行。那么,如何实现在浏览器中编写PHP代码并保持不被执行呢?下面将详细介绍。首先,

ANCOVA(协方差分析)是一种有用的统计方法,因为它可以在分析中包含协变量,这可以帮助调整辅助变量并增加组间比较的精确度。这些额外的因素或协变量可以通过使用ANCOVA将其纳入研究中。为了确保观察到的组间差异是由研究中的治疗或干预引起的,而不是由无关因素引起的,可以使用ANCOVA来调整协变量对组均值的影响。这可以使组间比较更准确,并给出更可靠的关于变量之间关系的结论。在本文中,我们将仔细研究ANCOVA并在Python中实施。什么是ANCOVA?协方差分析(ANCOVA)方法比较两个或多个组
