之前在一个比较繁忙的系统抓到的耗时长、消耗CPU多的一条SQL,如下:
SELECT * FROM Z_VISU_DATA_ALARM_LOG T
WHERE TO_DATE(T.T_TIMESTR, 'MM/DD/YY HH24:MI:SS'))<=(TO_DATE(TO_CHAR(SYSDATE, 'yyyy-mm-dd HH24:mi:ss'),'yyyy-mm-dd HH24:mi:ss') - 1800 * 1000 / 1440/60/1000

1.先看看奇葩的表设计:设计表的同学看来很喜欢varchar2这种数据类型,以及128这个数字。
SQL> desc Z_VISU_DATA_ALARM_LOG
Name Type Nullable Default Comments
-------------- ------------- -------- ------- --------
T_DESC VARCHAR2(128) Y
T_ERRORSTRING VARCHAR2(128) Y
T_KEY VARCHAR2(128) Y
T_POINTNAME VARCHAR2(128) Y
T_PTNAMEEXT VARCHAR2(128) Y
T_PTNAMELONG VARCHAR2(128) Y
T_PTTIME VARCHAR2(128) Y
T_PTTIMEMS VARCHAR2(128) Y
T_RAWSTATUS VARCHAR2(128) Y
T_RETURNSTATUS VARCHAR2(128) Y
T_STATUS VARCHAR2(128) Y
T_TIMEMSSTR VARCHAR2(128) Y
T_TIMESTR VARCHAR2(128) Y
T_UNITS VARCHAR2(128) Y
T_VALSTR VARCHAR2(128) Y
T_VALUE VARCHAR2(128) Y

2.再看看记录数:看到这么多数据再加上表名,猜测这个是一个记录alarm log的大表,真想问一下历史数据归档是怎么做的,虽然明知道得不到答案。
SQL> select count(*) from Z_VISU_DATA_ALARM_LOG;
COUNT(*)
----------
7971800

3.最后看下这个SQL的执行计划:其实不用看执行计划也能猜到是全表扫描。因为SQL写的太随意了!写的时候只是为了完成功能,而不去考虑性能。奇葩的表设计+800W记录+SQL做全表扫描能不慢?能不占用cpu高?

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
Plan hash value: 3652682256
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)
--------------------------------------------------------------------------------
| 0 | DELETE STATEMENT | | 701K| 1683K| 42632 (22)
| 1 | DELETE | Z_VISU_DATA_ALARM_LOG | | |
|* 2 | TABLE ACCESS FULL| Z_VISU_DATA_ALARM_LOG | 701K| 1683K| 42632 (22)
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter((TO_DATE(TO_CHAR(SYSDATE@!,'yyyy-mm-dd HH24:mi:ss'),'yyyy-mm-dd
HH24:mi:ss')-TO_DATE("T"."T_TIMESTR",'MM/DD/YY HH24:MI:SS'))*24*60
15 rows selected

SQL>

那要怎么优化这个SQL呢?
1.表设计的时候,时间字段还是用date或者timestamp吧。BTW,t_desc只有128个字符够吗?
2.根据查询的时间间隔来做分区表,这样对表只需要做ALTER TABLE xxx EXCHANGE就可以完成历史数据归档,也可以降低不必要的IO开销
3.直接优化这个SQL:
(1)首先改写,将=号左右两边做下数学变换+移动一下位置
SELECT * FROM Z_VISU_DATA_ALARM_LOG T
WHERE TO_DATE(T.T_TIMESTR, 'MM/DD/YY HH24:MI:SS'))<=(TO_DATE(TO_CHAR(SYSDATE, 'yyyy-mm-dd HH24:mi:ss'), 'yyyy-mm-dd HH24:mi:ss') - 1800 * 1000 / 1440/60/1000
(2)对 TO_DATE(T.T_TIMESTR, 'MM/DD/YY HH24:MI:SS'))建立函数索引。

这个有个梗,有可能会遇到ORA-01743:only pure functions can be indexed 。
对于这个错误是因为创建TO_DATE(T.T_TIMESTR, 'MM/DD/YY HH24:MI:SS'))的时候因为最后年份YY只取了后两位,这是一个不确定的值,故而会报错。故需要改成对TO_DATE(T.T_TIMESTR, 'yyyy-mm-dd HH24:MI:SS'))创建函数索引,当然sql也需要改。


PS:以下是tom大师对ORA-01743错误的一个说明
One quirk I have noticed with function-based indexes is that if you create one on the built-in
function TO_DATE, it will not succeed in some cases, for example:
ops$tkyte@ORA10GR1> create table t ( year varchar2(4) );
Table created.
ops$tkyte@ORA10GR1> create index t_idx on t( to_date(year,'YYYY') );
create index t_idx on t( to_date(year,'YYYY') )
*
ERROR at line 1:
ORA-01743: only pure functions can be indexed
This seems strange, since we can sometimes create a function using TO_DATE, for example:
ops$tkyte@ORA10GR1> create index t_idx on t( to_date('01'||year,'MMYYYY') );
Index created.
The error message that accompanies this isn’t too illuminating either:
ops$tkyte@ORA10GR1> !oerr ora 1743
01743, 00000, "only pure functions can be indexed"
// *Cause: The indexed function uses SYSDATE or the user environment.
// *Action: PL/SQL functions must be pure (RNDS, RNPS, WNDS, WNPS). SQL
// expressions must not use SYSDATE, USER, USERENV(), or anything
// else dependent on the session state. NLS-dependent functions
// are OK.
We are not using SYSDATE. We are not using the “user environment” (or are we?). No
PL/SQL functions are used, and nothing about the session state is involved. The trick lies in
the format we used: YYYY. That format, given the same exact inputs, will return different
, anytime in the month of May
ops$tkyte@ORA10GR1> select to_char( to_date('2005','YYYY'),
2 'DD-Mon-YYYY HH24:MI:SS' )
3 from dual;
TO_CHAR(TO_DATE('200
--------------------
01-May-2005 00:00:00
the YYYY format will return May 1, in June it will return June 1, and so on. It turns out that
TO_DATE, when used with YYYY, is not deterministic! That is why the index cannot be created: it
would only work correctly in the month you created it in (or insert/updated a row in). So, it is
due to the user environment, which includes the current date itself.
To use TO_DATE in a function-based index, you must use a date format that is unambigu-
ous and deterministic—regardless of what day it is currently.

优化一个奇葩表设计上的全表扫描SQL的更多相关文章

  1. 2015-10-22 前思后想,决定重构表结构,免得这个APP死在数据表设计上

    新的设计稿出来了,如下,原来旧的是第二张       -------  原来的评论级数只有2级,现在是不限,2级的意思是,用户评论该帖是一级,用户的评论能被人评论,这是第2级,评论评论的评论不能够再被 ...

  2. 一个可能是世界上最全的 API 接口集合库开源项目

    对于程序员来说,为自己的程序选择一些合适的API并不是那么简单,有时候还会把你搞得够呛,今天猿妹要和大家分享一个开源项目,这个项目汇集了各种开发的api,涵盖了音乐.新闻.书籍.日历等,无论你是从事W ...

  3. 年终巨献 史上最全 ——LINQ to SQL语句

    LINQ to SQL语句(1)之Where 适用场景:实现过滤,查询等功能. 说明:与SQL命令中的Where作用相似,都是起到范围限定也就是过滤作用的,而判断条件就是它后面所接的子句.Where操 ...

  4. 史上最全 ——LINQ to SQL语句

    LINQ to SQL语句(1)之Where 适用场景:实现过滤,查询等功能. 说明:与SQL命令中的Where作用相似,都是起到范围限定也就是过滤作用的,而判断条件就是它后面所接的子句.Where操 ...

  5. MaxCompute表设计最佳实践

    MaxCompute表设计最佳实践 产生大量小文件的操作 MaxCompute表的小文件会影响存储和计算性能,因此我们先介绍下什么样的操作会产生大量小文件,从 而在做表设计的时候考虑避开此类操作. 使 ...

  6. 索引法则--LIKE以%开头会导致索引失效进而转向全表扫描(使用覆盖索引解决)

    Mysql 系列文章主页 =============== 1 准备数据 1.1 建表 DROP TABLE IF EXISTS staff; CREATE TABLE IF NOT EXISTS st ...

  7. Oracle 11g全表扫描以Direct Path Read方式执行

    在Oracle Database 11g中有一个新特性,全表扫描可以通过直接路径读的方式来执行(Direct Path Read),这是一个合理的变化,如果全表扫描的大量数据读取是偶发性的,则直接路径 ...

  8. Acitiviti数据库表设计(学习笔记)

    ACT_ID_*:与权限,用户与用户组,以及用户与用户组关系相关的表 ACT_RU_*:代表了流程引擎运行时的库表,RU表示Runtime ACT_HI_*:HI表示History当流程完成了节点以后 ...

  9. Oracle 数据库禁止全表访问的时候direct path read /////

    一般在OLAP环境中,大表在进行全表扫描的时候一般会出现direct path read等待事件,如果在OLTP环境中,出现大量的direct path read直接路径读取,这样就有问题了.一般在O ...

随机推荐

  1. Ubuntu总结常用命令记录

    功能: 如何看系统是多少位:命令行下uname -a 桌面下打开终端:Ctrl + Alt + T    Linux下查看: 白色:表示普通文件蓝色:表示目录绿色:表示可执行文件红色:表示压缩文件浅蓝 ...

  2. oracle一次给多表添加相同字段

    遇到一个需求:在已经建好的数据库中,为每一个数据表都添加相同的3个字段. 分析:数据库中的数据表较多,一一手动修改耗时低效,是否可以用程序遍历每一张表,然后为遍历到的当前表添加字段? 查询当前用户的所 ...

  3. 光驱SSD安装Win7+ubuntu系统双系统

    准备条件: U盘,32GB,三星品牌 SSD,120GB,三星品牌 win7旗舰版,Ghost系统(安装简单嘛),Ylmf_Ghost_Win7_SP1_x64_2016_1011.iso ubunt ...

  4. SQL Sever无法打开链接对话框,未将对象引用设置到对象的实例。(AppIDPackage)

    前几天刚做完系统,先装的是SQL Sever2008,装完后还试了一下,OK~没问题,然后就继续装VS2012等一些软件.搞到很晚没有继续试试就睡了,第二天运行SSMS出问题了..(如图 1.0 所示 ...

  5. JDBC值事务

    事务的四大特性: 原子性, 一致性(比如说A给B转账,A转了之后B的账户增加了,两个都完成才叫一致性),隔离性(A给B转账,A给C转账,AB和AC并发是无关的),永久性(转账之后 不可能复原,就是说不 ...

  6. python的反转(切片)

    看下面代码吧,简单来说不如直接看代码.如下: #coding=utf-8 __author__ = 'debude' a = 'python' print a[::-1] #从最后n开始,每走一位都打 ...

  7. $q -- AngularJS中的服务(理解)

      描述 译者注: 看到了一篇非常好的文章,如果你有兴趣,可以查看: Promises与Javascript异步编程 , 里面对Promises规范和使用情景,好处讲的非常好透彻,个人觉得简单易懂. ...

  8. Debian8升级4.5内核

    本文讲述如何升级Debian8的内核到4.5版本 0x01:去linux kernel官网https://www.kernel.org/下载4.5的内核,选择tar.xz格式 0x02:想办法把下载好 ...

  9. $_SERVER 详情

    $_SERVER['HTTP_ACCEPT_LANGUAGE']//浏览器语言 $_SERVER['REMOTE_ADDR'] //当前用户 IP . $_SERVER['REMOTE_HOST'] ...

  10. jdbc链接数据库的三种方式

    /** * jdbc连接数据库 * @author APPle * */ public class Demo1 { //连接数据库的URL private String url = "jdb ...