[20120214]异常数据导致执行计划改变.txt

简介: 今天上午,用户反应一条sql执行有点慢。我检查发现,原来使用索引的语句现在变成了全表扫描,而且昨晚oracle数据库自动分析过这个表。语句很复杂,抽取有问题的部分:SELECT *  FROM med_operation_schedule a WHERE (  ...
今天上午,用户反应一条sql执行有点慢。我检查发现,原来使用索引的语句现在变成了全表扫描,而且昨晚oracle数据库自动分析过这个表。

语句很复杂,抽取有问题的部分:

SELECT *
  FROM med_operation_schedule a
 WHERE (       scheduled_date_time >= TO_DATE ('2012-02-15 00:00', 'yyyy-mm-dd hh24:mi')
           AND scheduled_date_time         OR scheduled_date_time IS NULL
        OR NVL (emergency_indicator, 0) = 1 AND scheduled_date_time > TRUNC (SYSDATE, 'dd')
       )
执行计划是全表扫描。把那个or单独拆开来分析,发现这个条件走的是全表扫描NVL (emergency_indicator, 0) = 1 AND scheduled_date_time > TRUNC (SYSDATE, 'dd')。奇怪!这个条件scheduled_date_time > TRUNC (SYSDATE, 'dd')的记录不会很多。

select * from med_operation_schedule a where a.scheduled_date_time > TRUNC (SYSDATE, 'dd');

这才发现原来里面存在一条scheduled_date_time='5011-7-17 16:30:00' 异常记录。

这样造成优化器认为大于TRUNC (SYSDATE, 'dd')的记录不会很多,执行计划选择全表扫描。

解决方法:
1.要求操作员更正数据,再分析表,这个不能保证以后不再出现,或者程序要做必要的检查,不能输入这样的日期。
2.在该字段建立直方图,不过10g很麻烦,后台的分析Method_Opt=> 'FOR ALL COLUMNS SIZE AUTO ',这样可能下一次分析直方图又会被取消。看来自己该修改自动分析的缺省参数为Method_Opt=> 'FOR ALL COLUMNS SIZE REPEAT'
3.我实在不想跟他们提,我选择的方法是修改统计信息。

方法如下:
1.取出表定义:
exp system/xxxx@yyyy tables=(zzzz.med_operation_schedule) rows=N file=med.dmp

2.过滤出脚本:[注意要加-3参数,具体看man strings文档,不然会丢失信息]
strings -3 med.dmp > med.txt

3.找到如下内容:

DECLARE  SREC DBMS_STATS.STATREC; BEGIN SREC.MINVAL := '303231363032'; SREC.MAXVAL := '303231363036'; SREC.EAVS := 0; SREC.CHVALS := NULL; #
SREC.NOVALS := DBMS_STATS.NUMARRAY(I
250248268640273000000000000000000000,250248268640292000000000000000000000&
); SREC.BKVALS := DBMS_STATS.NUMARRAY(
0,1
); SREC.EPC := 2; DBMS_STATS.SET_COLUMN_STATS(NULL,'"MED_OPERATION_SCHEDULE"','"OPERATING_ROOM"', NULL ,NULL,NULL,4,.25,0,srec,7,6); END;

--删除一些怪异的字符,重新排版,这样可以很好检查是否写错!
DECLARE
   srec   DBMS_STATS.statrec;
BEGIN
   srec.minval := '786E0B1E090101';
   srec.maxval := '966F0711111F01';
   srec.eavs := 0;
   srec.chvals := NULL;
   srec.novals := DBMS_STATS.numarray (2455531.33333333, 3551487.6875);
   srec.bkvals := DBMS_STATS.numarray (0,1);
   srec.epc := 2;
   DBMS_STATS.set_column_stats (NULL,
                                '"MED_OPERATION_SCHEDULE"',
                                '"SCHEDULED_DATE_TIME"',
                                NULL,
                                NULL,
                                NULL,
                                7555,
                                .000132362673726009,
                                0,
                                srec,
                                8,
                                6
                               );
END;

4.最大最小如何修改呢?需要了解srec.minval以及srec.maxval转换。google找到如下链接:
http://mwidlake.wordpress.com/2010/02/24/update-to-decoding-high-and-low-values/

SELECT column_name, data_type, low_value,high_value, density,rtrim(
               to_char(100*(to_number(substr(low_value,1,2),'XX')-100)
                      + (to_number(substr(low_value,3,2),'XX')-100),'fm0000')||'-'||
               to_char(to_number(substr(low_value,5,2),'XX'),'fm00')||'-'||
               to_char(to_number(substr(low_value,7,2),'XX'),'fm00')||' '||
               to_char(to_number(substr(low_value,9,2),'XX')-1,'fm00')||':'||
               to_char(to_number(substr(low_value,11,2),'XX')-1,'fm00')||':'||
               to_char(to_number(substr(low_value,13,2),'XX')-1,'fm00')) l,
               rtrim(
               to_char(100*(to_number(substr(high_value,1,2),'XX')-100)
                      + (to_number(substr(high_value,3,2),'XX')-100),'fm0000')||'-'||
               to_char(to_number(substr(high_value,5,2),'XX'),'fm00')||'-'||
               to_char(to_number(substr(high_value,7,2),'XX'),'fm00')||' '||
               to_char(to_number(substr(high_value,9,2),'XX')-1,'fm00')||':'||
               to_char(to_number(substr(high_value,11,2),'XX')-1,'fm00')||':'||
               to_char(to_number(substr(high_value,13,2),'XX')-1,'fm00')) h
  FROM dba_tab_cols
 WHERE table_name = 'MED_OPERATION_SCHEDULE' AND column_name = 'SCHEDULED_DATE_TIME'

COLUMN_NAME,DATA_TYPE,LOW_VALUE,HIGH_VALUE,DENSITY,L,H
SCHEDULED_DATE_TIME,DATE,786E0B1E090101,966F0711111F01,0.000132362673726009,2010-11-30 08:00:00,5011-07-17 16:30:00,

--比较麻烦。放弃这样算的方法!

5.采用建立一个表的方法,在测试库建立:
export NLS_DATE_FORMAT="YYYY-MM-DD HH24:MI:SS"

SQL> create table t(vd date);
Table created.

SQL> insert into t values('2010-11-30 08:00:00');
1 row created.

SQL> insert into t values('2012-2-15 18:00:00');
1 row created.

SQL> commit;
Commit complete.

SQL> exec dbms_stats.gather_table_stats(ownname=>user, tabname=> 't', cascade=> true, estimate_percent=> null, method_opt=> 'FOR ALL COLUMNS SIZE 1');
PL/SQL procedure successfully completed.

SQL> column column_name format a10
SQL> column data_type format a10
SQL> column lower_value format a20
SQL> column high_value format a20
SQL> SELECT column_name, data_type, low_value, high_value, density   FROM dba_tab_cols WHERE table_name = 'T' AND column_name = 'VD';
COLUMN_NAM DATA_TYPE  LOW_VALUE                                                        HIGH_VALUE              DENSITY
---------- ---------- ---------------------------------------------------------------- -------------------- ----------
VD         DATE       786E0B1E090101                                                   7870020F130101               .5

HIGH_VALUE=7870020F130101

这个如何修改呢?srec.novals := DBMS_STATS.numarray (2455531.33333333, 3551487.6875);
还是使用emp的方法(略),与上面相同:

SREC.NOVALS := DBMS_STATS.NUMARRAY(2455531.33333333,2455973.75)

最后修改如下:

DECLARE
   srec   DBMS_STATS.statrec;
BEGIN
   srec.minval := '786E0B1E090101';
   srec.maxval := '7870020F130101';
   srec.eavs := 0;
   srec.chvals := NULL;
--   srec.novals := DBMS_STATS.numarray (2455531.33333333, 3551487.6875);
   SREC.NOVALS := DBMS_STATS.NUMARRAY(2455531.33333333,2455973.75);
   srec.bkvals := DBMS_STATS.numarray (0,1);
   srec.epc := 2;
   DBMS_STATS.set_column_stats (NULL,
                                '"MED_OPERATION_SCHEDULE"',
                                '"SCHEDULED_DATE_TIME"',
                                NULL,
                                NULL,
                                NULL,
                                7555,
                                .000132362673726009,
                                0,
                                srec,
                                8,
                                6
                               );
END;

6.在测试上面的sql语句,发现可以使用索引了。只不过加了bitmap convert+bitmap or操作。

7.锁定以后不分析表。
BEGIN
  SYS.DBMS_STATS.LOCK_TABLE_STATS (
      OwnName        => 'MEDSURGERY'
     ,TabName        => 'MED_OPERATION_SCHEDULE');
END;
/

整个优化完成!

8.BTW最终没有选择这样的方式,我还是建立了直方图。仅仅是为了学习!

9.补充学习:
关于SREC.NOVALS := DBMS_STATS.NUMARRAY(2455531.33333333,2455973.75);里面的数字,表示的是Julian format, ie number of days since 1st Jan 4712BC.
http://mwidlake.wordpress.com/2010/02/06/format-of-date-histograms/

SQL> select 2455973.75+to_date(1,'J')-1 from dual;

2455973.75+TO_DATE
-------------------
2012-02-15 18:00:00

SQL>  select TO_DATE(TRUNC(2455973.75),'J')+(2455973.75-TRUNC(2455973.75)) from dual;
TO_DATE(TRUNC(24559
-------------------
2012-02-15 18:00:00

反过来呢?如果知道日期如何转换呢?'J'仅仅取到整数部分。修改如下:

SQL> select to_char(to_date('2012-02-15 18:00:00','YYYY-MM-DD HH24:MI:SS') ,'J')+to_date('2012-02-15 18:00:00','YYYY-MM-DD HH24:MI:SS')
-trunc(to_date('2012-02-15 18:00:00','YYYY-MM-DD HH24:MI:SS')) x from dual
SQL> /
         X
----------
2455973.75

目录
相关文章
|
9月前
|
机器学习/深度学习 人工智能 运维
CodeFuse团队2024年10篇论文总结
CodeFuse 是蚂蚁集团开发的多语言代码大型语言模型(LLM),基于海量高质量代码数据和多任务微调技术,已在内部研发人员的编码、测试、运维等场景中广泛应用。2024年,CodeFuse 在国际顶会如ICSE、ICDE、KDD等发表多篇论文,涵盖CodeLLM、机器学习、AI等领域,并开源多个自研大模型,总下载量近200万。项目持续迭代,欢迎贡献和建议。
353 11
|
JSON API 数据库
json-server的简单使用
如何使用`json-server`来快速搭建一个模拟API服务,包括安装`json-server`、创建JSON数据文件、启动服务以及通过浏览器测试API的基本操作。
168 0
json-server的简单使用
|
11月前
|
JSON JavaScript 前端开发
harmony-chatroom 自研纯血鸿蒙OS Next 5.0聊天APP实战案例
HarmonyOS-Chat是一个基于纯血鸿蒙OS Next5.0 API12实战开发的聊天应用程序。这个项目使用了ArkUI和ArkTS技术栈,实现了类似微信的消息UI布局、输入框光标处插入文字、emoji表情图片/GIF动图、图片预览、红包、语音/位置UI、长按语音面板等功能。
782 3
|
9月前
|
机器学习/深度学习 算法 前端开发
图解前向、反向传播算法,一看就懂!
前向传播是神经网络中信息从输入层经过隐藏层传递到输出层的过程。每个神经元接收前一层的输出,通过加权求和和激活函数处理后传递给下一层,最终生成预测结果。此过程涉及输入信号、加权求和、激活函数应用等步骤。前向传播用于生成预测结果,在训练阶段与真实标签比较以计算损失函数,并在推理阶段直接生成预测值。反向传播则利用链式法则计算损失函数相对于权重的梯度,调整参数以减小误差,从而优化模型性能。两者结合实现神经网络的有效训练和预测。
|
7月前
|
JSON 缓存 数据格式
【Azure Fabric Service】分享使用Visual Studio 2022发布中国区Service Fabric服务应用的办法
本文介绍了在Visual Studio 2022中无法直接创建Service Fabric Cluster服务时的替代方案。通过使用PowerShell命令或修改Cloud.xml文件,可将应用部署到已创建的SF Cluster。具体步骤包括:1) 在Azure门户创建Service Fabric服务并安装客户端证书;2) 获取服务端和客户端证书指纹;3) 修改Cloud.xml中的ClusterConnectionParameters后发布应用。最后附有参考资料以供进一步学习。
108 5
|
监控 安全 测试技术
确保第三方API安全的5个最佳实践
确保第三方API安全的5个最佳实践
|
Linux 网络安全 数据安全/隐私保护
|
人工智能 搜索推荐 算法
📈业绩飙升的秘密:AI驱动的个性化营销策略,职场营销新高度!
【8月更文挑战第1天】在激烈的商业竞争中,AI驱动的个性化营销正成为企业突破的关键。通过大数据收集用户多维度信息,形成精准用户画像;利用智能推荐系统实现“千人千面”,大幅提升用户满意度和转化率;并通过实时优化策略快速响应市场变化。简化的Python示例展示了基于用户画像的推荐算法应用。AI营销以其高效灵活的特点,已成为职场营销的新常态,助力企业在数据时代掌握营销主动权。
223 0
|
安全
【工具】分析邮箱信息工具emlAnalyzer
在国内我甚至找不到这个工具的中文介绍,可见我们对这方面的轻视,事实上,每年关于邮箱攻击的事件不计其数,因此,我们应该好好了解一番。
876 0
|
存储 分布式计算 监控
Hadoop 之 HDFS 进程详解
你好看官,里面请!今天笔者讲的是Hadoop 之 HDFS 进程详解。不懂或者觉得我写的有问题可以在评论区留言,我看到会及时回复。 注意:本文仅用于学习参考,不可用于商业用途,如需转载请跟我联系。
694 1