- 浏览: 595420 次
- 性别:
- 来自: 厦门
文章分类
- 全部博客 (669)
- oracle (36)
- java (98)
- spring (48)
- UML (2)
- hibernate (10)
- tomcat (7)
- 高性能 (11)
- mysql (25)
- sql (19)
- web (42)
- 数据库设计 (4)
- Nio (6)
- Netty (8)
- Excel (3)
- File (4)
- AOP (1)
- Jetty (1)
- Log4J (4)
- 链表 (1)
- Spring Junit4 (3)
- Autowired Resource (0)
- Jackson (1)
- Javascript (58)
- Spring Cache (2)
- Spring - CXF (2)
- Spring Inject (2)
- 汉字拼音 (3)
- 代理模式 (3)
- Spring事务 (4)
- ActiveMQ (6)
- XML (3)
- Cglib (2)
- Activiti (15)
- 附件问题 (1)
- javaMail (1)
- Thread (19)
- 算法 (6)
- 正则表达式 (3)
- 国际化 (2)
- Json (3)
- EJB (3)
- Struts2 (1)
- Maven (7)
- Mybatis (7)
- Redis (8)
- DWR (1)
- Lucene (2)
- Linux (73)
- 杂谈 (2)
- CSS (13)
- Linux服务篇 (3)
- Kettle (9)
- android (81)
- protocol (2)
- EasyUI (6)
- nginx (2)
- zookeeper (6)
- Hadoop (41)
- cache (7)
- shiro (3)
- HBase (12)
- Hive (8)
- Spark (15)
- Scala (16)
- YARN (3)
- Kafka (5)
- Sqoop (2)
- Pig (3)
- Vue (6)
- sprint boot (19)
- dubbo (2)
- mongodb (2)
最新评论
No SQL,No cost. SQL语句是造成数据库开销最大的部分。而不良SQL写法直接导致数据库系统性能下降的情形比比皆是。那么如何才能称得
上高效的SQL语句呢?一是查询优化器为当前的SQL语句生成最佳的执行计划,保证数据读写使用最佳路径;二是设置合理的物理存储结构,如表
的类型,字段的顺序,字段的数据类型等。本文主要描述如何编写高效的SQL语句并给出示例。下面的描述主要分为三个部分,一是编写高效SQL
语句,二是使用索引提高查询性能的部分,三是总结部分。
一、编写高效SQL语句
二、合理使用索引以提高性能
索引依赖于表而存在,是真实表的一个缩影,类似于一本书的目录,通过目录以更快获得所需的结果。Oracle使用了一个复杂的自平衡
B数据结构。即任意记录的DML操作将打破索引的平衡,而定期重构索引使得索引重新获得平衡。通常,通过索引查找数据比全表扫描更高效。
任意的DQL或DML操作,SQL优化引擎优先使用索引来计算当前操作的成本以生成最佳的执行计划。一旦使用索引操出参数optimizer_index_cost_adj
设定的值才使用全表扫描。同样对于多表连接使用索引也可以提高效率。同时索引也提供主键(primary key)的唯一性验证。
除了那些LONG或LONG RAW数据类型,你可以索引几乎所有的列.通常,在大型表中使用索引特别有效.当然,你也会发现,在扫描小表时,使用索
引同样能提高效率。
虽然使用索引能得到查询效率的提高,但是索引需要空间来存储,需要定期维护.尤其是在有大量DML操作的表上,任意的DML操作都将引起索
引的变更这意味着每条记录的INSERT , DELETE , UPDATE将为此多付出4 , 5 次的磁盘I/O . 因为索引需要额外的存储空间和处理,
那些不必要的索引反而会使查询反应时间变慢。
DML操作使用索引上存在碎片而失去高度均衡,因此定期的重构索引是有必要的。
三、总结
1、尽可能最小化基表数据以及中间结果集(通过过滤条件避免后续产生不必要的计算与聚合)
2、为where子句中的谓词信息提供最佳的访问路径(rowid访问,索引访问)
3、使用合理的SQL写法来避免过多的Oracle内部开销以提高性能
4、合理的使用提示以提高表之间的连接来提高连接效率(如避免迪卡尔集,将不合理的嵌套连接改为hash连接等)
转自:http://blog.csdn.net/leshami/article/details/7406672
上高效的SQL语句呢?一是查询优化器为当前的SQL语句生成最佳的执行计划,保证数据读写使用最佳路径;二是设置合理的物理存储结构,如表
的类型,字段的顺序,字段的数据类型等。本文主要描述如何编写高效的SQL语句并给出示例。下面的描述主要分为三个部分,一是编写高效SQL
语句,二是使用索引提高查询性能的部分,三是总结部分。
一、编写高效SQL语句
1) 选择最有效的表名顺序(仅适用于RBO模式) ORACLE的解析器总是按照从右到左的顺序处理FROM子句中的表名,因此FROM子句中最后的一个表将作为驱动表被优先处理。当FROM子句 存在多个表的时候,应当考虑将表上记录最少的那个表置于FROM的最右端作为基表。Oracle会首先扫描基表(FROM子句中最后的那个表)并对 记录进行排序,然后扫描第二个表(FROM子句中最后第二个表),最后将所有从第二个表中检索出的记录与第一个表中合适记录进行合并。如 果有3个以上的表连接查询, 那就需要选择交叉表(intersection table)作为基础表,交叉表是指那个被其他表所引用的表。 下面的例子使用最常见的scott或hr模式下的表进行演示 表 EMP 有14条记录 表 DEPT 有4条记录 SELECT /*+ rule */ COUNT( * ) FROM emp, dept; --高效的写法 scott@CNMMBO> set autotrace traceonly stat; scott@CNMMBO> SELECT /*+ rule */ COUNT( * ) FROM emp, dept; Elapsed: 00:00:00.14 Statistics ---------------------------------------------------------- 1 recursive calls 0 db block gets 35 consistent gets 0 physical reads 0 redo size 515 bytes sent via SQL*Net to client 492 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed SELECT /*+ rule */ COUNT( * ) FROM dept, emp; --低效的写法 scott@CNMMBO> SELECT /*+ rule */ COUNT( * ) FROM dept, emp; Elapsed: 00:00:00.02 Statistics ---------------------------------------------------------- 1 recursive calls 0 db block gets 105 consistent gets 0 physical reads 0 redo size 515 bytes sent via SQL*Net to client 492 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed 2) select 查询中避免使用'*' 当你想在SELECT子句中列出所有的COLUMN时,使用动态SQL列引用 '*' 是一个方便的方法.不幸的是,这是一个非常低效的方法.实际 上,ORACLE在解析的过程中, 会将 '*' 依次转换成所有的列名, 这个工作是通过查询数据字典完成的, 这意味着将耗费更多的时间。 注:本文中的例子出于简化演示而使用了select * ,生产环境应避免使用. 3) 减少访问数据库的次数 每当执行一条SQL语句,Oracle 需要完成大量的内部操作,象解析SQL语句,估算索引的利用率,绑定变量, 读数据块等等.由此可 见,减少访问数据库的次数,实际上是降低了数据库系统开销 -->下面通过3种方式来获得雇员编号为7788与7902的相关信息 -->方式 1 (最低效): select ename,job,sal from emp where empno=7788; select ename,job,sal from emp where empno=7902; -->方式 2 (次低效): -->下面使用了参数游标来完成,每传递一次参数则需要对表emp访问一次,增加了I/O DECLARE CURSOR C1(E_NO NUMBER) IS SELECT ename, job, sal FROM emp WHERE empno = E_NO; BEGIN OPEN C1 (7788); FETCH C1 INTO …, …, …; .. OPEN C1 (7902); FETCH C1 INTO …, …, …; CLOSE C1; END; -->方式 3 (最高效) SELECT a.ename , a.job , a.sal , b.ename , b.job , b.sal FROM emp a, emp b WHERE a.empno = 7788 OR b.empno = 7902; 注意:在SQL*Plus,SQL*Forms和Pro*C中重新设置ARRAYSIZE参数,可以增加每次数据库访问的检索数据量,建议值为200. 4) 使用DECODE函数来减少处理时间 -->使用decode函数可以避免重复扫描相同的行或重复连接相同的表 select count(*),sum(sal) from emp where deptno=20 and ename like 'SMITH%'; select count(*),sum(sal) from emp where deptno=30 and ename like 'SMITH%'; -->通过使用decode函数一次扫描即可完成所有满足条件记录的处理 SELECT COUNT( DECODE( deptno, 20, 'x', NULL ) ) d20_count , COUNT( DECODE( deptno, 30, 'x', NULL ) ) d30_count , SUM( DECODE( deptno, 20, sal, NULL ) ) d20_sal , SUM( DECODE( deptno, 30, sal, NULL ) ) d30_sal FROM emp WHERE ename LIKE 'SMITH%'; 类似的,DECODE函数也可以运用于GROUP BY 和ORDER BY子句中。 5) 整合简单,无关联的数据库访问 -->如果你有几个简单的数据库查询语句,你可以把它们整合到一个查询中以提高性能(即使它们之间没有关系) -->整合前 SELECT name FROM emp WHERE empno = 1234; SELECT name FROM dept WHERE deptno = 10; SELECT name FROM cat WHERE cat_type = 'RD'; -->整合后 SELECT e.name, d.name, c.name FROM cat c , dpt d , emp e , dual x WHERE NVL( 'X', x.dummy ) = NVL( 'X', e.ROWID(+) ) AND NVL( 'X', x.dummy ) = NVL( 'X', d.ROWID(+) ) AND NVL( 'X', x.dummy ) = NVL( 'X', c.ROWID(+) ) AND e.emp_no(+) = 1234 AND d.dept_no(+) = 10 AND c.cat_type(+) = 'RD'; -->从上面的SQL语句可以看出,尽管三条语句被整合为一条,性能得以提高,然可读性差,此时应权衡性能与代价 6) 删除重复记录 -->通过使用rowid来作为过滤条件,性能高效 DELETE FROM emp e WHERE e.ROWID > (SELECT MIN( x.ROWID ) FROM emp x WHERE x.empno = e.empno); 7) 使用truncate 代替 delete -->通常情况下,任意记录的删除需要在回滚段构造删除前镜像以实现回滚(rollback).对于未提交的数据在执行rollback之后,Oracle会生成 -->等价SQL语句去恢复记录(如delete,则生成对应的insert语句;如insert则生成对应的delete;如update,则是同时生成delete和insert -->使用truncate命令则是执行DDL命令,不产生任何回滚信息,直接格式化并释放高水位线.故该语句性能高效.由于不能rollback,因此慎用. 8) 尽量多使用COMMIT(COMMIT应确保事务的完整性) -->只要有可能,在程序中尽量多使用COMMIT,这样程序的性能得到提高,需求也会因为COMMIT所释放的资源而减少 -->COMMIT所释放的资源: -->1.回滚段上用于恢复数据的信息 -->2.释放语句处理期间所持有的锁 -->3.释放redo log buffer占用的空间(commit将redo log buffer中的entries 写入到联机重做日志文件) -->4.ORACLE为管理上述3种资源中的内部开销 9) 计算记录条数 -->一般的情况下,count(*)比count(1)稍快.如果可以通过索引检索,对索引列的计数是最快的,因为直接扫描索引即可,例如COUNT(EMPNO) -->实际情况是经测试上述三种情况并无明显差异. 10) 用Where子句替换HAVING子句 -->尽可能的避免having子句,因为HAVING 子句是对检索出所有记录之后再对结果集进行过滤。这个处理需要排序,总计等操作 -->通过WHERE子句则在分组之前即可过滤不必要的记录数目,从而减少聚合的开销 -->低效: SELECT deptno, AVG( sal ) FROM emp GROUP BY deptno HAVING deptno = 20; scott@CNMMBO> SELECT deptno, AVG( sal ) 2 FROM emp 3 GROUP BY deptno 4 HAVING deptno= 20; Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 7 consistent gets 0 physical reads 0 redo size 583 bytes sent via SQL*Net to client 492 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed -->高效: SELECT deptno, AVG( sal ) FROM emp WHERE deptno = 20 GROUP BY deptno; scott@CNMMBO> SELECT deptno, AVG( sal ) 2 FROM emp 3 WHERE deptno = 20 4 GROUP BY deptno; Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 2 consistent gets 0 physical reads 0 redo size 583 bytes sent via SQL*Net to client 492 bytes received via SQL*Net from client 2 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 1 rows processed 11) 最小化表查询次数 -->在含有子查询的SQL语句中,要特别注意减少对表的查询 -->低效: SELECT * FROM employees WHERE department_id = (SELECT department_id FROM departments WHERE department_name = 'Marketing') AND manager_id = (SELECT manager_id FROM departments WHERE department_name = 'Marketing'); -->高效: SELECT * FROM employees WHERE ( department_id, manager_id ) = (SELECT department_id, manager_id FROM departments WHERE department_name = 'Marketing') -->类似更新多列的情形 -->低效: UPDATE employees SET job_id = ( SELECT MAX( job_id ) FROM jobs ), salary = ( SELECT AVG( min_salary ) FROM jobs ) WHERE department_id = 10; -->高效: UPDATE employees SET ( job_id, salary ) = ( SELECT MAX( job_id ), AVG( min_salary ) FROM jobs ) WHERE department_id = 10; 12) 使用表别名 -->在多表查询时,为所返回列使用表别名作为前缀以减少解析时间以及那些相同列歧义引起的语法错误 13) 用EXISTS替代IN 在一些基于基础表的查询中,为了满足一个条件,往往需要对另一个表进行联接.在这种情况下,使用EXISTS(或NOT EXISTS)通常 将提高查询的效率. -->低效: SELECT * FROM emp WHERE sal > 1000 AND deptno IN (SELECT deptno FROM dept WHERE loc = 'DALLAS') -->高效: SELECT * FROM emp WHERE empno > 1000 AND EXISTS (SELECT 1 FROM dept WHERE deptno = emp.deptno AND loc = 'DALLAS') 14) 用NOT EXISTS替代NOT IN 在子查询中,NOT IN子句引起一个内部的排序与合并.因此,无论何时NOT IN子句都是最低效的,因为它对子查询中的表执行了一个全表 遍历.为避免该情形,应当将其改写成外部连接(OUTTER JOIN)或适用NOT EXISTS -->低效: SELECT * FROM emp WHERE deptno NOT IN (SELECT deptno FROM dept WHERE loc = 'DALLAS'); -->高效: SELECT e.* FROM emp e WHERE NOT EXISTS (SELECT 1 FROM dept WHERE deptno = e.deptno AND loc = 'DALLAS'); -->最高效(尽管下面的查询最高效,并不推荐使用,因为列loc使用了不等运算,当表dept数据量较大,且loc列存在索引的话,则此时索引失效) SELECT e.* FROM emp e LEFT JOIN dept d ON e.deptno = d.deptno WHERE d.loc <> 'DALLAS' 15) 使用表连接替换EXISTS 一般情况下,使用表连接比EXISTS更高效 -->低效: SELECT * FROM employees e WHERE EXISTS (SELECT 1 FROM departments WHERE department_id = e.department_id AND department_name = 'IT'); -->高效: SELECT * -->经测试此写法SQLplus下比上面的写法多一次逻辑读,而在Toad下两者结果一致 FROM employees e INNER JOIN departments d ON d.department_id = e.department_id WHERE d.department_name = 'IT'; 16) 用EXISTS替换DISTINCT 对于一对多关系表信息查询时(如部门表和雇员表),应避免在select 子句中使用distinct,而使用exists来替换 -->低效: SELECT DISTINCT e.department_id, d.department_name FROM departments d INNER JOIN employees e ON d.department_id = e.department_id; -->高效: SELECT d.department_id,department_name from departments d WHERE EXISTS (SELECT 1 FROM employees e WHERE d.department_id=e.department_id); EXISTS 使查询更为迅速,因为RDBMS核心模块将在子查询的条件一旦满足后,立刻返回结果 -->经测试此写法SQLplus下比上面的写法多一次逻辑读,而在Toad下两者结果一致 17) 使用 UNION ALL 替换 UNION(如果有可能的话) 当SQL语句需要UNION两个查询结果集时,这两个结果集合会以UNION-ALL的方式被合并, 然后在输出最终结果前进行排序。 如果用UNION ALL替代UNION, 这样排序就不是必要了。 效率就会因此得到提高。 注意: UNION ALL会输出所有的结果集,而UNION则过滤掉重复记录并对其进行排序.因此在使用时应考虑业务逻辑是否允许当前的结果集存在重复现象 寻找低效的SQL语句 -->下面的语句主要适用于从视图v$sqlarea中获得当前运行下且耗用buffer_gets较多的SQL语句 SELECT executions , disk_reads , buffer_gets , ROUND( ( buffer_gets - disk_reads ) / buffer_gets, 2 ) hit_ratio , ROUND( disk_reads / executions, 2 ) reads_per_run , sql_text FROM v$sqlarea WHERE executions > 0 AND buffer_gets > 0 AND ( buffer_gets - disk_reads ) / buffer_gets < 0.80 ORDER BY 4 DESC; 18) 尽可能避免使用函数,函数会导致更多的 recursive calls
二、合理使用索引以提高性能
索引依赖于表而存在,是真实表的一个缩影,类似于一本书的目录,通过目录以更快获得所需的结果。Oracle使用了一个复杂的自平衡
B数据结构。即任意记录的DML操作将打破索引的平衡,而定期重构索引使得索引重新获得平衡。通常,通过索引查找数据比全表扫描更高效。
任意的DQL或DML操作,SQL优化引擎优先使用索引来计算当前操作的成本以生成最佳的执行计划。一旦使用索引操出参数optimizer_index_cost_adj
设定的值才使用全表扫描。同样对于多表连接使用索引也可以提高效率。同时索引也提供主键(primary key)的唯一性验证。
除了那些LONG或LONG RAW数据类型,你可以索引几乎所有的列.通常,在大型表中使用索引特别有效.当然,你也会发现,在扫描小表时,使用索
引同样能提高效率。
虽然使用索引能得到查询效率的提高,但是索引需要空间来存储,需要定期维护.尤其是在有大量DML操作的表上,任意的DML操作都将引起索
引的变更这意味着每条记录的INSERT , DELETE , UPDATE将为此多付出4 , 5 次的磁盘I/O . 因为索引需要额外的存储空间和处理,
那些不必要的索引反而会使查询反应时间变慢。
DML操作使用索引上存在碎片而失去高度均衡,因此定期的重构索引是有必要的。
1) 避免基于索引列的计算 where 子句中的谓词上存在索引,而此时基于该列的计算将使得索引失效 -->低效: SELECT employee_id, first_name FROM employees WHERE employee_id + 10 > 150; -->索引列上使用了计算,因此索引失效,走全表扫描方式 -->高效: SELECT employee_id, first_name FROM employees WHERE employee_id > 160; -->走索引范围扫描方式 例外情形 上述规则不适用于SQL中的MIN和MAX函数 hr@CNMMBO> SELECT MAX( employee_id ) max_id 2 FROM employees 3 WHERE employee_id 4 + 10 > 150; 1 row selected. Execution Plan ---------------------------------------------------------- Plan hash value: 1481384439 --------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 4 | 1 (0)| 00:00:01 | | 1 | SORT AGGREGATE | | 1 | 4 | | | | 2 | FIRST ROW | | 5 | 20 | 1 (0)| 00:00:01 | |* 3 | INDEX FULL SCAN (MIN/MAX)| EMP_EMP_ID_PK | 5 | 20 | 1 (0)| 00:00:01 | --------------------------------------------------------------------------------------------- 2) 避免在索引列上使用NOT运算或不等于运算(<>,!=) 通常,我们要避免在索引列上使用NOT或<>,两者会产生在和在索引列上使用函数相同的影响。 当ORACLE遇到NOT或不等运算时,他就会停止 使用索引转而执行全表扫描。 -->低效: SELECT * FROM emp WHERE NOT ( deptno = 20 ); -->实际上NOT ( deptno = 20 )等同于deptno <> 20,即deptno <>同样会限制索引 -->高效: SELECT * FROM emp WHERE deptno > 20 OR deptno < 20; -->尽管此方式可以替换且实现上述结果,但依然走全表扫描,如果是单纯的 > 或 < 运算,则此时为索引范围扫描 需要注意的是,在某些时候, ORACLE优化器会自动将NOT转化成相对应的关系操作符 其次如果是下列运算符进行NOT运算,依然有可能选择走索引, 仅仅除了NOT = 之外,因为 NOT = 等价于 <> “NOT >” to <= “NOT >=” to < “NOT <” to >= “NOT <=” to > 来看一个实际的例子 hr@CNMMBO> SELECT * 2 FROM employees 3 where not employee_id<100; -->索引列上使用了not,但是该查询返回了所有的记录,即107条,因此此时选择走全表扫描 107 rows selected. Execution Plan ---------------------------------------------------------- Plan hash value: 1445457117 ------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 107 | 7276 | 3 (0)| 00:00:01 | |* 1 | TABLE ACCESS FULL| EMPLOYEES | 107 | 7276 | 3 (0)| 00:00:01 | -->执行计划中使用了走全表扫描方式 ------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter("EMPLOYEE_ID">=100) -->查看这里的谓词信息被自动转换为 >= 运算符 hr@CNMMBO> SELECT * 2 FROM employees 3 where not employee_id<140; -->此例与上面的语句相同,仅仅是查询范围不同返回67条记录,而此时选择了索引范围扫描 67 rows selected. Execution Plan ---------------------------------------------------------- Plan hash value: 603312277 --------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 68 | 4624 | 3 (0)| 00:00:01 | | 1 | TABLE ACCESS BY INDEX ROWID| EMPLOYEES | 68 | 4624 | 3 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | EMP_EMP_ID_PK | 68 | | 1 (0)| 00:00:01 | -->索引范围扫描方式 --------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 2 - access("EMPLOYEE_ID">=140) 3) 用UNION 替换OR(适用于索引列) 通常情况下,使用UNION 替换WHERE子句中的OR将会起到较好的效果.基于索引列使用OR使得优化器倾向于使用全表扫描,而不是扫描索引. 注意,以上规则仅适用于多个索引列有效。 如果有column没有被索引, 查询效率可能会因为你没有选择OR而降低。 -->低效: SELECT deptno, dname FROM dept WHERE loc = 'DALLAS' OR deptno = 20; -->高效: SELECT deptno, dname FROM dept WHERE loc = 'DALLAS' UNION SELECT deptno, dname FROM dept WHERE deptno = 30 -->经测试,由于数据量较少,此时where子句中的谓词上都存在索引列时,两者性能相当. -->假定where子句中存在两列 scott@CNMMBO> create table t6 as select object_id,owner,object_name from dba_objects where owner='SYS' and rownum<1001; scott@CNMMBO> insert into t6 select object_id,owner,object_name from dba_objects where owner='SCOTT' and rownum<6; scott@CNMMBO> create index i_t6_object_id on t6(object_id); scott@CNMMBO> create index i_t6_owner on t6(owner); scott@CNMMBO> insert into t6 select object_id,owner,object_name from dba_objects where owner='SYSTEM' and rownum<=300; scott@CNMMBO> commit; scott@CNMMBO> exec dbms_stats.gather_table_stats('SCOTT','T6',cascade=>true); scott@CNMMBO> select owner,count(*) from t6 group by owner; OWNER COUNT(*) -------------------- ---------- SCOTT 5 SYSTEM 300 SYS 1000 scott@CNMMBO> select * from t6 where owner='SCOTT' and rownum<2; OBJECT_ID OWNER OBJECT_NAME ---------- -------------------- -------------------- 69450 SCOTT T_TEST scott@CNMMBO> select * from t6 where object_id=69450 or owner='SYSTEM'; 301 rows selected. Execution Plan ---------------------------------------------------------- Plan hash value: 238853296 ----------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ----------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 300 | 7200 | 5 (0)| 00:00:01 | | 1 | CONCATENATION | | | | | | | 2 | TABLE ACCESS BY INDEX ROWID| T6 | 1 | 24 | 2 (0)| 00:00:01 | |* 3 | INDEX RANGE SCAN | I_T6_OBJECT_ID | 1 | | 1 (0)| 00:00:01 | |* 4 | TABLE ACCESS BY INDEX ROWID| T6 | 299 | 7176 | 3 (0)| 00:00:01 | |* 5 | INDEX RANGE SCAN | I_T6_OWNER | 300 | | 1 (0)| 00:00:01 | ----------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - access("OBJECT_ID"=69450) 4 - filter(LNNVL("OBJECT_ID"=69450)) 5 - access("OWNER"='SYSTEM') Statistics ---------------------------------------------------------- 0 recursive calls 0 db block gets 46 consistent gets 0 physical reads 0 redo size 11383 bytes sent via SQL*Net to client 712 bytes received via SQL*Net from client 22 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 301 rows processed scott@CNMMBO> select * from t6 where owner='SYSTEM' or object_id=69450; 301 rows selected. Execution Plan ---------------------------------------------------------- Plan hash value: 238853296 ----------------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ----------------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 300 | 7200 | 5 (0)| 00:00:01 | | 1 | CONCATENATION | | | | | | | 2 | TABLE ACCESS BY INDEX ROWID| T6 | 1 | 24 | 2 (0)| 00:00:01 | |* 3 | INDEX RANGE SCAN | I_T6_OBJECT_ID | 1 | | 1 (0)| 00:00:01 | |* 4 | TABLE ACCESS BY INDEX ROWID| T6 | 299 | 7176 | 3 (0)| 00:00:01 | |* 5 | INDEX RANGE SCAN | I_T6_OWNER | 300 | | 1 (0)| 00:00:01 | ----------------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 3 - access("OBJECT_ID"=69450) 4 - filter(LNNVL("OBJECT_ID"=69450)) 5 - access("OWNER"='SYSTEM') Statistics ---------------------------------------------------------- 1 recursive calls 0 db block gets 46 consistent gets 0 physical reads 0 redo size 11383 bytes sent via SQL*Net to client 712 bytes received via SQL*Net from client 22 SQL*Net roundtrips to/from client 0 sorts (memory) 0 sorts (disk) 301 rows processed scott@CNMMBO> select * from t6 2 where object_id=69450 3 union 4 select * from t6 5 where owner='SYSTEM'; 301 rows selected. Execution Plan ---------------------------------------------------------- Plan hash value: 370530636 ------------------------------------------------------------------------------------------------ | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------------------ | 0 | SELECT STATEMENT | | 301 | 7224 | 7 (72)| 00:00:01 | | 1 | SORT UNIQUE | | 301 | 7224 | 7 (72)| 00:00:01 | | 2 | UNION-ALL | | | | | | | 3 | TABLE ACCESS BY INDEX ROWID| T6 | 1 | 24 | 2 (0)| 00:00:01 | |* 4 | INDEX RANGE SCAN | I_T6_OBJECT_ID | 1 | | 1 (0)| 00:00:01 | | 5 | TABLE ACCESS BY INDEX ROWID| T6 | 300 | 7200 | 3 (0)| 00:00:01 | |* 6 | INDEX RANGE SCAN | I_T6_OWNER | 300 | | 1 (0)| 00:00:01 | ------------------------------------------------------------------------------------------------ Predicate Information (identified by operation id): --------------------------------------------------- 4 - access("OBJECT_ID"=69450) 6 - access("OWNER"='SYSTEM') Statistics ---------------------------------------------------------- 1 recursive calls 0 db block gets 7 consistent gets 0 physical reads 0 redo size 11383 bytes sent via SQL*Net to client 712 bytes received via SQL*Net from client 22 SQL*Net roundtrips to/from client 1 sorts (memory) 0 sorts (disk) 301 rows processed -->从上面的统计信息可知,consistent gets由46下降为7,故当where子句中谓词上存在索引时,使用union替换or更高效 -->即使当列object_id与owner上不存在索引时,使用union仍然比or更高效(在Oracle 10g R2与Oracle 11g R2测试) 4) 避免索引列上使用函数 -->下面是一个来自实际生产环境的例子 -->表acc_pos_int_tbl上business_date列存在索引,由于使用了SUBSTR函数,此时索引失效,使用全表扫描 SELECT acc_num , curr_cd , DECODE( '20110728' , ( SELECT TO_CHAR( LAST_DAY( TO_DATE( '20110728', 'YYYYMMDD' ) ), 'YYYYMMDD' ) FROM dual ), 0 , adj_credit_int_lv1_amt + adj_credit_int_lv2_amt - adj_debit_int_lv1_amt - adj_debit_int_lv2_amt ) AS interest FROM acc_pos_int_tbl WHERE SUBSTR( business_date, 1, 6 ) = SUBSTR( '20110728', 1, 6 ) AND business_date <= '20110728'; -->改进的办法 SELECT acc_num , curr_cd , DECODE( '20110728' , ( SELECT TO_CHAR( LAST_DAY( TO_DATE( '20110728', 'YYYYMMDD' ) ), 'YYYYMMDD' ) FROM dual ), 0 , adj_credit_int_lv1_amt + adj_credit_int_lv2_amt - adj_debit_int_lv1_amt - adj_debit_int_lv2_amt ) AS interest FROM acc_pos_int_tbl acc_pos_int_tbl WHERE business_date >= TO_CHAR( LAST_DAY( ADD_MONTHS( TO_DATE( '20110728', 'yyyymmdd' ), -1 ) ) + 1, 'yyyymmdd' ) AND business_date <= '20110728'; -->下面的例子虽然没有使用函数,但字符串连接同样导致索引失效 -->低效: SELECT account_name, amount FROM transaction WHERE account_name || account_type = 'AMEXA'; -->高效: SELECT account_name, amount FROM transaction WHERE account_name = 'AMEX' AND account_type = 'A'; 5) 比较不匹配的数据类型 -->下面的查询中business_date列上存在索引,且为字符型,这种 -->低效: SELECT * FROM acc_pos_int_tbl WHERE business_date = 20090201; Execution Plan ---------------------------------------------------------- Plan hash value: 2335235465 ------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 37516 | 2857K| 106K (1)| 00:21:17 | |* 1 | TABLE ACCESS FULL| ACC_POS_INT_TBL | 37516 | 2857K| 106K (1)| 00:21:17 | ------------------------------------------------------------------------------------- Predicate Information (identified by operation id): --------------------------------------------------- 1 - filter(TO_NUMBER("BUSINESS_DATE")=20090201) -->这里可以看到产生了类型转换 -->高效: SELECT * FROM acc_pos_int_tbl WHERE business_date = '20090201' 6) 索引列上使用 NULL 值 IS NULL和IS NOT NULL会限制索引的使用,因为数据中没有值等于NULL值,即便是NULL值也不等于NULL值.且NULL值不存储在于索引之中 因此应尽可能避免在索引类上使用NULL值 SELECT acc_num , pl_cd , order_qty , trade_date FROM trade_client_tbl WHERE input_date IS NOT NULL; Execution Plan ---------------------------------------------------------- Plan hash value: 901462645 -------------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | -------------------------------------------------------------------------------------- | 0 | SELECT STATEMENT | | 1 | 44 | 15 (0)| 00:00:01 | |* 1 | TABLE ACCESS FULL| TRADE_CLIENT_TBL | 1 | 44 | 15 (0)| 00:00:01 | -------------------------------------------------------------------------------------- alter table trade_client_tbl modify (input_date not null); 不推荐使用的查询方式 SELECT * FROM table_name WHERE col IS NOT NULL SELECT * FROM table_name WHERE col IS NULL 推荐使用的方式 SELECT * FROM table_name WHERE col >= 0 --尽可能的使用 =, >=, <=, like 等运算符 -->Author: Robinson Cheng -->Blog: http://blog.csdn.net/robinson_0612
三、总结
1、尽可能最小化基表数据以及中间结果集(通过过滤条件避免后续产生不必要的计算与聚合)
2、为where子句中的谓词信息提供最佳的访问路径(rowid访问,索引访问)
3、使用合理的SQL写法来避免过多的Oracle内部开销以提高性能
4、合理的使用提示以提高表之间的连接来提高连接效率(如避免迪卡尔集,将不合理的嵌套连接改为hash连接等)
转自:http://blog.csdn.net/leshami/article/details/7406672
发表评论
文章已被作者锁定,不允许评论。
-
sql 查询一些基础数据(不经常变)关联关系时可以把关联关系写入到表中
2018-04-16 15:18 433原来的查询语句 FROM table1 t LEFT ... -
sql 最小化表查询次(col1,col2) = (select col1,col2 from t)
2018-01-02 16:51 552最小化表查询次数 ... -
sql 用inner join取代in和exists
2018-01-02 16:48 1152使用表连接替换EXISTS ... -
oracle HINT详解
2017-09-01 10:42 519ORACLE的HINT详解 hints是oracle提供的 ... -
oracle jdbctemplate如何优化查询速度
2017-08-21 09:33 13181.设置setFetchSize进行优化 // 可以优化 ... -
oracle Parallel模式-Parallel用法(/*+PARALLEL(n)*/)
2017-08-18 16:48 18991.用途 强行启用 ... -
oracle 存储过程学习总结
2016-08-22 08:42 359dbms_output.put_line('CONCAT ... -
oracle SGA总结信息
2016-07-06 10:38 511SGA 系统全局区 SGA是Oracle用来为实例村粗数据和 ... -
oracle where条件分支查询
2016-06-30 16:16 521select * from test xx where ... -
oracle with as用法
2016-06-24 09:58 937with as语法 –针对一个别名 with tmp as ... -
oracle 重复数据处理的sql整理
2016-06-22 14:44 398Oracle 删除重复数据只留一条 1、查找表中多余的重 ... -
oracle 数据库组成部分(数据库(各种文件)+实例(SGA PGA + 后台进程))知识
2016-04-11 19:51 1269一、SQL server中的实例与 ... -
oracle expdp与impdp导入导出
2016-03-17 10:17 1283[b]数据泵工具导出的步骤:[/b] 1、创建DIRECT ... -
oracle 常用sql语句积累
2016-01-28 11:01 5701.删除重复列 delete from t t1 where ... -
oracle forall关键字进行批量操作
2014-12-12 15:26 1045两个数据操纵语言(DML)语句:BULK COLLECT和FO ... -
oracle 表中有子父关系列则使用CONNECT BY PRIOR
2014-10-27 10:08 420查询名字为xx xxx的所有子数据 SELECT re.U ... -
oracle 存储过程使用实例
2014-10-22 17:41 6071.建包 CREATE OR REPLACE PACKA ... -
oracle 删除大量数据后整理表(analyze table xxx compute statistics)
2014-10-17 18:09 22271. DELETE 后 TRUNCATE TABLE ; 然 ... -
oracle splitstr 函数
2014-10-15 17:45 1409splitstr函数 CREATE OR REPLACE ... -
oracle merge 更新或插入数据
2014-09-26 14:26 494主要功能 提供有条件地更新和插入数据到数据库表中 如果该 ...
相关推荐
Oracle 高效 SQL 语句原则 Oracle 高效 SQL 语句原则是指在编写 Oracle 数据库 SQL 语句时需要遵循的一些基本原则,以便提高 SQL 语句的执行效率,减少数据库服务器的负载,提高应用程序的性能。下面是 Oracle 高效...
本项目提供了一个Java源码工具,能够帮助用户便捷地将Oracle SQL语句转换为MySQL SQL语句。 Oracle SQL与MySQL SQL的主要差异在于以下几个方面: 1. **数据类型**:Oracle支持的数据类型如NUMBER、LONG、RAW等在...
总结来说,Oracle数据库SQL语句跟踪器是一个强大的性能分析工具,它通过Toad for Oracle等工具提供深入的洞察,帮助数据库管理员和开发者有效地优化数据库性能,提升系统的整体响应速度和稳定性。通过实时监控、性能...
本文将深入探讨如何利用压力测试工具来评估和优化Oracle数据库中的SQL语句性能。 标题"压测Oracle的SQL语句的性能情况"暗示了我们关注的是在高负载情况下,Oracle数据库处理SQL查询的能力。压力测试(Pressure ...
以下是对"Oracle的SQL语句的一些经验总结"中可能涉及的关键知识点的详细阐述: 1. **基本查询**:SQL的基础是SELECT语句,用于从表中提取数据。例如,`SELECT column1, column2 FROM table_name;`用于获取指定列的...
本文档总结了 Oracle 中常用的 SQL 语句,包括数据库的创建、删除、备份、表的创建、删除、修改、索引的创建、视图的创建等基本操作,以及一些高级查询运算符的使用。 一、数据库操作 * 创建数据库:CREATE ...
Oracle 常用 SQL 语句汇总 Oracle 是一个功能强大且复杂的关系数据库管理系统,它提供了多种 SQL 语句来管理和操作数据库。在本文中,我们将详细介绍 Oracle 中常用的 SQL 语句,包括数据控制语句(DML)、数据定义...
oracle的SQL语句调优总结,Oracle语句优化53个规则详解。
对ORACLE-SQL进行一些布局优化,更新它的格式
### Oracle高效SQL语句原则 在Oracle数据库管理与优化的过程中,高效的SQL语句编写是非常重要的一个环节。本文将根据提供的信息“Oracle高效SQL语句原则”进行深入解析,并结合具体的实践经验和理论知识来阐述如何...
Oracle SQL Profiler,自己设计算法写的一款非常好用的抓取Oracle数据库SQL语句的工具,可以再没有源码的情况下监控ORACLE数据库服务器的v$sqlarea视图抓取出从点击开始按钮到点击结束按钮期间执行过的SQL语句。...
Oracle数据库是全球最广泛使用的数据库管理系统之一,它遵循SQL标准,提供了一套强大的SQL语句,使得数据库操作变得高效和灵活。在Oracle数据库中,SQL语句是与数据库进行交互的主要方式。本文将总结一些Oracle基本...
### Oracle监听执行SQL语句详解 #### 一、Oracle监听执行概述 在Oracle数据库管理与维护过程中,有时候我们需要了解应用程序正在执行哪些SQL语句,这不仅有助于性能优化,还可以帮助我们诊断潜在的问题。通过监听...
本文将深入探讨Oracle中SQL语句执行效率的查找与解决方法,特别关注于如何识别和优化那些导致性能瓶颈的查询。 ### Oracle SQL执行效率:查找与解决 #### 一、资源消耗分析 在Oracle中,SQL语句执行效率低下通常...
oracle、sql语句基础
Oracle中用sql语句创建数据库,比较长,一般做了解
oracle中SQL语句优化
Oracle数据库Sql语句详解大全,提供给大家快速查询复习哦!