优化sql语句

认识Oracle的执行过程

Oracle优化法则---漏斗法则

常规SQL语句优化

  1、建议不用“*”来代替所有列名

  SELECT语句中可以用“*”来列出某个表的所有列名,但是这样的写法对Oracle系统来说会存在解析的动态问题。Oracle系统会通过查询数据字典来将“*”转换成表的所有列名,这自然会消耗系统时间。建议用户在写SELECT语句时,采用与访问表有关的实际列名。

  2、用TRUNCATE代替DELETE

  当使用DELETE删除表中的数据行时,Oracle会使用撤销表空间(UNDO TABLESPACE)来存放恢复的信息。在这期间,如果用户没有发出COMMIT语句,而是发出ROLLBACK语句,Oracle系统会将数据恢复到删除之前的状态。当用户使用TRUNCATE语句对表的数据进行删除时,系统不会将被删除的数据写到回滚段(或撤销表空间)中,速度当然要快得多。所以当希望对表或者簇中的所有行全部删除时,采用TRUNCATE命令更加有效,其语法格式如下:

Truncate [table | cluster] schema.[table_name] [cluster_name] [drop | reuse storage]

  3、在确保完整性的情况下多用COMMIT语句

  在PL/SQL块中,经常将几个相互联系的DML语句写在一个BEGIN…END块中,建议在每个块的END前面使用COMMIT语句,这样就可以实现对DML语句的及时提交,同时也释放事务所占用的资源。
COMMIT所释放的资源如下。
  1)回滚段上用于恢复数据的信息,撤销表空间也只做短暂的保留
  2)被程序语句获得的锁
  3)redo log buffer中的空间
  4)Oracle为管理上述资源的内部花费

  4、尽量减少表的查询次数

  低效率的SQL查询语句:

select empno,ename,job from emp
where deptno in (select deptno from dept where loc = 'BEIJING')
or deptno in (select deptno from dept where loc = 'NEW YORK');
--要对dept表执行两遍的查询

  对上面的代码进行适当修改,高效率的SQL查询语句:

select empno,ename,job from emp
where deptno in (select deptno from dept where loc = 'BEIJING' or loc = 'NEW YORK')

 5、子查询建议使用 [NOT] EXISTS 代替 [NOT] IN

  在子查询中,[not] in子句将执行一个内部的排序与合并,无论在哪种情况下,NOT IN都是最低效的 (因为它对子查询中的表执行了一个全表遍历). 为了避免使用NOT IN ,我们可以把它改写成外连接(Outer Joins)或NOT EXISTS. 

--低效SQL
SELECT empno,ename,job FROM EMP WHERE  DEPTNO IN(SELECT DEPTNO FROM DEPT WHERE LOC = 'NANJING')
--高效SQL
SELECT empno,ename,job FROM EMP WHERE EXISTS (SELECT deptno FROM DEPT WHERE LOC != 'NANJING') 

  一分为二看[not] in,当[not] in后面跟子查询,并且查询的结果集较多时,不宜使用[not] in;如果[not] in后面的括号内时列表或子查询所满足结果集很少时,也是可以使用的。

  6、用>=替代>

--低效:
   SELECT empno,ename,job FROM EMP WHERE DEPTNO > 3

--高效:
   SELECT empno,ename,job FROM EMP  WHERE DEPTNO >= 4

  DEPTNO > 3时ORACLE会先找出为3的记录索引再进行比较,而DEPTNO >= 4时ORACLE则直接找到=4的记录索引

  7、用in代替or

-- 低效
select.. from emp where empno = 10 or empno = 20 or empno = 30

-- 高效:
select.. from emp where empno in (10,20,30);

  8、避免使用耗费资源的操作

  带有DISTINCT,UNION,MINUS,INTERSECT,ORDER BY的SQL语句会启动SQL引擎执行耗费资源的排序(SORT)功能. DISTINCT需要一次排序操作, 而其他的至少需要执行两次排序.

  例如,一个UNION查询,其中每个查询都带有GROUP BY子句, GROUP BY会触发嵌入排序(NESTED SORT) ; 这样, 每个查询需要执行一次排序, 然后在执行UNION时, 又一个唯一排序(SORT UNIQUE)操作被执行而且它只能在前面的嵌入排序结束后才能开始执行. 嵌入的排序的深度会大大影响查询的效率.

  9、order by

  任何在Order by语句的非索引项或者有计算表达式都将降低查询速度

  经常用于排序的字段应加上索引

  10、避免在索引列上使用计算

  WHERE子句中,如果索引列是函数的一部分.优化器将不使用索引而使用全表扫描.

--低效:
SELECTFROM  EMP WHERE SAL * 12 > 25000;

--高效:
SELECTFROM EMP WHERE SAL > 25000/12;

  11、通过使用>=、<=等,避免使用NOT命令

select * from employee where salary <> 3000; --不能使用索引

对这个查询,可以改写为不使用NOT:

select * from employee where salary<3000 or salary>3000; --允许Oracle对salary列使用索引

  12、字符型字段的引号

  比如表中PHONE_NO字段是CHAR型,而且创建有索引,但在WHERE条件中忘记了加引号,就不会用到索引。

  WHERE PHONE_NO=‘13920202022’

  WHERE PHONE_NO=13920202022

  13、避免在索引列上使用IS NULL和IS NOT NULL 

  14、模糊查询

  下面模糊查询也将导致全表扫描:  
  select id from t where name like '%abc%'  

表连接优化

  1、驱动表的选择

  驱动表(Driving Table)是指被最先访问的表(通常以全表扫描的方式被访问)。Oracle优化器会检查SQL语句中的每个表的物理大小、索引状态,然后选用花费最低的执行路径,接下来,我们分析下面的一个例子。

select s.Name,d.Dept_Name
from Department d ,Students s
where d.Dept_No = s.Dept_No;

  在上面的代码中,假设在Students表的dept_no列创建了索引,而在Department表的dept_no列没有索引。由于Department最先被访问(紧随from其后),这样Department表将被作为查询中的驱动表,由此可见,只有两个表都建立有索引,优化器才能按照紧随from关键字后面的驱动表的规则(form最后的表)来对待。

  2、WHERE子句中的连接顺序: 
  ORACLE采用自下而上的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他WHERE条件之前, 那些可以过滤掉最大数量记录的条件必须写在WHERE子句的末尾.

 合理使用索引

  创建索引的基本原则:

  1)以查询关键字为基础,表中的行随机排序。

  2)包含的列数相对比较少的表。

  3)表中的大多数查询都包含相对简单的WHERE从句。

  4)对于经常以查询关键字为基础的表,并且该表中的行遵从均匀分布

  5)缓存命中率低,并且不需要操作系统权限。

  在创建索引时,选择列和表达式是非常重要的,下面是创建索引时选择索引列的原则:

  1)WHERE从句频繁使用的关键字。

  2)SQL语句中频繁由于进行表连接的关键字。

  3)可选择性高(重复性少的)关键字。

  4)对于取值较少的关键字或表达式,不要采用标准的B树索引,可以考虑建立位图索引。

  5)不要将那些频繁修改的列作为索引列。

  6)不要使用包含操作符或函数的WHERE从句中的关键字作为索引列,如果需要的话,可以考虑建立函数索引。

  7)如果大量并发的INSERT、UPDATE、DELETE语句访问了父表或者子表,则考虑使用完整性约束的外部键作为索引。

  8)在选择索引列时,还要考虑该索引所引起的INSERT、UPDATE、DELETE操作是否值得。

  选择复合索引主列  

  多列索引也叫做复合索引,复合索引有时比单列索引有更好的性能。如果在建立索引时采用了几个列作为索引,则在使用时也要按照建立时的顺序来描述,也就是说,主列是最先被选择的列。

       例如,为tb_test表创建一个复合索引complex_index,该索引包括column1、column2、column3个列(并且建立顺序亦此),如果把这3列作为where查询条件,那么这3个列的最优排序方式如下:

create index complex_index on tb_test(column1,column2,column3)

select * from tb_test where column1 > 0 and column2 > 0 and column3 < 0

  避免全表扫描大表

  监视索引是否被使用

   1、设置监视索引 ALTER INDEX ... MONITORING USAGE

   2、检查索引使用情况 SELECT * FROM V$OBJECT_USAGE;

     3、如索引在限定的时间内得不到使用,建议使用drop index 删除该索引

优化器的使用

   Oracle优化器在处理每一个SQL语句准备执行之前,都需要进行许多步骤才能使SQL语句成为可执行的语句。

  1)语法检查:检查SQL语句的拼写是否正确。

  2)语义分析:核实所有与数据字典不一致的表和列的名字。

  3)概要存储检查:检查数据字典,以确定该SQL语句的概要是否已经存在。

  4)生成执行计划:使用基于成本的优化规则和数据字典的统计表来决定最佳执行计划。

  5)建立二进制代码:基于执行计划,Oracle生成了二进制执行代码。

  在PL/SQL Developer中写好一段SQL代码后,按F5,PL/SQL Developer会自动打开执行计划窗口,显示该SQL的执行计划。

 

根据Operation缩进来判断,缩进最多的最先执行;(缩进相同时,最上面的最先执行)

ID: 一个序号,但不是执行的先后顺序。执行的先后根据缩进来判断。

Operation: 当前操作的内容。

Rows: 当前操作的Cardinality,Oracle估计当前操作的返回结果集。

Cost(CPU):Oracle 计算出来的一个数值(代价),用于说明SQL执行的代价。

Time:Oracle 估计当前操作的时间。

在看执行计划的时候,除了看执行计划本身,还需要看谓词和统计信息。 通过整体信息来判断SQL效率。

Access :

  • 通过某种方式定位了需要的数据,然后读取出这些结果集,叫做Access。
  • 表示这个谓词条件的值将会影响数据的访问路劲(表还是索引)。

Filter:

  • 把所有的数据都访问了,然后过滤掉不需要的数据,这种方式叫做filter 。
  • 表示谓词条件的值不会影响数据的访问路劲,只起过滤的作用。

在谓词中主要注意access,要考虑谓词的条件,使用的访问路径是否正确。

表访问的几种方式:(非全部)

  • TABLE ACCESS FULL(全表扫描)
  • TABLE ACCESS BY ROWID(通过ROWID的表存取)
  • TABLE ACCESS BY INDEX SCAN(索引扫描)

索引扫描又分五种:

  • INDEX UNIQUE SCAN(索引唯一扫描)
  • INDEX RANGE SCAN(索引范围扫描)
  • INDEX FULL SCAN(索引全扫描)
  • INDEX FAST FULL SCAN(索引快速扫描)
  • INDEX SKIP SCAN(索引跳跃扫描)

表连接的几种方式:

  • SORT MERGE JOIN(排序-合并连接)
  • NESTED LOOPS(嵌套循环)
  • HASH JOIN(哈希连接)
  • CARTESIAN PRODUCT(笛卡尔积)

HASH JOIN的三种模式:

  • OPTIMAL HASH JOIN
  • ONEPASS HASH JOIN
  • MULTIPASS HASH JOIN

Oracle数据库和SQL重演

  Database Replay是指在产品环境的数据库上捕获所有负载,并可以将之传送至备份的(Standby)数据库或有备份恢复的测试库上,在测试环境中重演主库的环境,这使得升级或软件更新可以进行预先的“真实”测试,或者可以通过测试环境完全再现真实环境的负载及运行情况。

  这是Oracle向后追溯能力的又一增强,在Oracle 10g中,我们知道Oracle通过V$SESSION_WAIT_HISTORY视图、ASH特性等,实现将数据库的等待时间向后追溯。现在通过Databse Replay特性,Oracle可以将整个数据库的负载捕获、记录并实现Replay,也就是增强了整个数据库的向后追溯能力。

  这一特性提供了再现现场能力,极大地丰富了用户发现并解决数据库问题的手段,将为数据库管理带来更多的方便之处。

  当然使用这一特性会带来一定的性能负担,Oracle说这一负担在5%左右。

  这一特性的简化版本就是SQL Replay,即只捕获SQL负载,通过SQL负载,应用程序可以再现SQL影响,如下图所示。

Oracle性能顾问

SQL调优顾问

  SQL Tuning Advisor(SQL调优顾问)是Oracle 10g中引入的,设计它的目的就是为了替代传统的手工SQL调整。

  SQL调优顾问处理的对象包括那些响应时间很慢或者是占用CPU/DISK很高的SQL。

  SQL调优顾问收集这些SQL,并且给出自己的建议,它包括下面的部分:

    1)怎样调整SQL的执行计划。

    2)优化后效率的提升幅度。

    3)做出这条建议的理论原理。

    4)直接给出推荐使用的命令。

      用户可以有选择性地接收这些建议,然后去调优SQL。

 

  随着SQL调优顾问的引入,用户现在就可以让Oracle优化器来自动地调整SQL。

--授权
grant administer any sql tuning set to scott;
grant advisor to scott;
grant create any sql profile to scott;
grant alter any sql profile to scott;
grant drop any sql profile to scott;

--创建任务
declare
tuning_task_name VARCHAR2(30);
tuning_sqltext CLOB;
begin
 tuning_sqltext := 'select job from emp';--注意,这里不支持*,要写上具体的字段名
 tuning_task_name := DBMS_SQLTUNE.CREATE_TUNING_TASK(
    sql_text  => tuning_sqltext,
    user_name => 'SCOTT',
    scope  => 'COMPREHENSIVE',
    time_limit => 60,
    task_name => 'sql_trace_20131124',
    description => 'EMP SELECT TUNE');
end;

--所有已经创建任务的查看
select * from user_advisor_log;

-- 任务的执行
exec dbms_sqltune.execute_tuning_task(task_name => 'sql_trace_20131124');

--任务执行后状态的检查
 select * from user_advisor_tasks t where t.task_name = 'sql_trace_20131124'

--最终报告的生成
select dbms_sqltune.report_tuning_task('sql_trace_20131124') from dual;

--任务的删除
exec dbms_sqltune.drop_tuning_task('sql_trace_20131124');

SQL访问顾问

  SQL访问顾问(SQL Access Advisor)的设计目的是获得有关基于实际频率和使用类型(而非数据类型)进行分区、索引和创建物化视图以改进模式设计的建议。这与SQL Tuning Advisor提供有关查询、调整及在流程中延长整个优化过程的建议有所不同,它的特点如下:

  1)分析整个负载而不仅仅是单独的SQL语句。

  2)使访问结构设计更加清晰,以优化应用程序性能。

  3)建议创建和删除某些索引、物化视图和物化视图日志以提高性能。

      Oracle 11g的SQL访问顾问除了可以像在Oracle 10g中一样分析索引、物化视图等,还可以分析表和查询以识别可能的分区策略,这在进行最佳模式设计时可以提供很大的帮助。在Oracle 11g中,SQL访问顾问可以提供与整个负载相关的建议,包括考虑创建成本和维护访问结构等。

 

 

posted @ 2021-04-17 22:35  Tiger-Adan  阅读(428)  评论(0编辑  收藏  举报