人生倒计时
- 今日已经过去小时
- 这周已经过去天
- 本月已经过去天
- 今年已经过去个月


1. SQL 语句执行步骤
语法分析 > 语义分析 > 视图转换 > 表达式转换 > 选择优化器 > 选择连接方法 > 选择连接顺序 > 选择数据搜索路径 > 运行“执行计划”
2. 选择合适的优化器 RULE(基于规则)、COST(基于成本)、(可选)
3. 访问表的方法 全表扫描 全表扫描是按顺序访问表中的每条记录,通过一次读取多个数据块来优化全表扫描。通过ROWID访问表 ROWID包含了表中记录的物理位置信息,并通过索引实现了数据与存储数据的物理位置(ROWID)的连接。通常,索引提供了一种快速访问ROWID的方法,因此那些基于索引列的查询可以在性能上得到提升。
4. 共享 SQL 语句
5. 选择最高效的表名序列 示例:表 TAB1 16,384 条记录 表 TAB2 1 条记录
/*选择TAB2作为基础表 (最好的方法)*/SELECT COUNT(*) FROM TAB1,TAB2/*执行时间0.96秒*//*选择TAB1作为基础表 (不佳的方法)*/SELECT COUNT(*) FROM TAB2,TAB1/*执行时间26.09秒*/
如果要加入查询的表超过3张,则需要选择交叉表(表)作为基表,交叉表是指被其他表引用的表。
/*高效的SQL*/SELECT * FROM LOCATION L, CATEGORY C, EMP EWHERE E.EMP_NO BETWEEN 1000 AND 2000AND E.CAT_NO = C.CAT_NOAND E.LOCN = L.LOCN会比下面的 SQL 更高效
/*低效的SQL*/SELECT * FROM EMP E, LOCATION L, CATEGORY CWHERE E.CAT_NO = C.CAT_NOAND E.LOCN = L.LOCNAND E.EMP_NO BETWEEN 1000 AND 20006. Where 子句中的连接顺序以自下而上或从右到左的顺序解析 WHERE 子句。根据这个原则,表之间的连接必须写在其他 WHERE 条件之前,而那些可以过滤掉最大记录数的条件必须写在 WHERE 子句的末尾。
/*低效,执行时间156.3秒*/SELECT Column1,Column2FROM EMP EWHERE E.SAL > 50000AND E.JOB = 'MANAGER'AND 25 <(SELECT COUNT(*) FROM EMPWHERE MGR = E.EMPNO)/*高效,执行时间10.6秒*/SELECT Column1,Column2FROM EMP EWHERE 25 < (SELECT COUNT(*) FROM EMPWHERE MGR=E.EMPNO)AND E.SAL > 50000AND E.JOB = 'MANAGER'避免在 7. 子句中使用“*”
8. 减少数据库访问次数 在每条SQL语句执行的时候,内部都做了很多工作:解析SQL语句>估计索引的利用率>绑定变量>读取数据块等。可以是看到减少访问数据库的次数,实际上可以减少工作量。
9. 整个简单的无关数据库访问如果有几个简单的数据库查询语句,可以将它们组合成一个查询(即使它们之间没有关系),以减少过多的数据库IO开销。这种方法虽然提高了效率,但是程序的可读性大大降低,所以还是要权衡利弊。
10. 使用代替
11.尽可能多的使用尽可能多的在程序中使用,这样可以提高程序的性能,由于资源的释放而减少需求。发布的资源:
12. 统计记录数
Select count(*) from tablename;Select count(1) from tablename;Select count(column) from tablename;一般认为,在没有主键索引的情况下,第二种 COUNT(1) 方法最快。如果只有一列,没有索引,COUNT(*) 更快。如果有索引列,当然,使用索引列 COUNT() 是最快的。
13. 将该子句替换为Where子句,避免该子句,只有在检索到所有记录后才过滤结果集。这种处理需要进行排序、求和等操作。可以通过 WHERE 子句限制记录的数量来减少这种开销。
14.减少对表的查询操作在包含子查询的SQL语句中,注意减少对表的查询操作。
/*低效SQL*/SELECT TAB_NAME FROM TABLESWHERE TAB_NAME =(SELECT TAB_NAME FROM TAB_COLUMNSWHERE VERSION = 604)AND DB_VER =(SELECT DB_VER FROM TAB_COLUMNSWHERE VERSION = 604)/*高效SQL*/SELECT TAB_NAME FROM TABLESWHERE (TAB_NAME,DB_VER)=(SELECT TAB_NAME,DB_VERFROM TAB_COLUMNSWHERE VERSION = 604)15. 使用表别名(Alias) 在一条SQL语句中连接多个表时,请使用表别名,并在每张表前加上别名。这将减少解析时间并减少那些由歧义引起的语法错误。歧义是指由于 SQL 中不同的表同名,当 SQL 语句中出现 this 时,SQL 解析器无法确定 this 的归属。
16. 使用代替IN 在很多基于底层表的查询中,为了满足一个条件,往往需要连接另一个表。在这种情况下,使用(或 NOT )通常会提高查询的效率。
/*低效SQL*/SELECT * FROM EMPWHERE EMPNO > 0AND DEPTNO IN (SELECT DEPTNO FROM DEPTWHERE LOC = 'MELB')/*高效SQL*/SELECT * FROM EMPWHERE EMPNO > 0AND EXISTS (SELECT 1FROM DEPTWHERE DEPT.DEPTNO = EMP.DEPTNOAND LOC = 'MELB')17. 使用NOT代替NOT IN 在子查询中,NOT IN子句会进行内部排序和合并,对子查询中的表进行全表遍历,效率非常低。为了避免使用 NOT IN,可以将其重写为 Outer Joins 或 NOT。
/*低效SQL*/SELECT * FROM EMPWHERE DEPT_NO NOT IN (SELECT DEPT_NO FROM DEPTWHERE DEPT_CAT='A')/*高效SQL*/SELECT * FROM EMP EWHERE NOT EXISTS (SELECT 1FROM DEPT DWHERE D.DEPT_NO = E.DEPT_NOAND DEPT_CAT ='A')18. 用表连接代替表连接通常比表连接更有效。
/*低效SQL*/SELECT ENAMEFROM EMP EWHERE EXISTS (SELECT 1FROM DEPTWHERE DEPT_NO = E.DEPT_NOAND DEPT_CAT = 'A')/*高效SQL*/SELECT ENAMEFROM DEPT D,EMP EWHERE E.DEPT_NO = D.DEPT_NOAND D.DEPT_CAT = 'A'19. 在提交包含多个表(例如部门表和员工表)信息的查询时,被替换为避免使用 in 子句。一般来说,您可以考虑将其替换为 EXIST。使查询更快,因为 RDBMS 核心模块会在子查询条件满足时立即返回结果。
/*低效SQL*/SELECT DISTINCT D.DEPT_NO,D.DEPT_NAMEFROM DEPT D,EMP EWHERE D.DEPT_NO = E.DEPT_NO/*高效SQL*/SELECT D.DEPT_NO,D.DEPT_NAMEFROM DEPT DWHERE EXISTS (SELECT 1FROM EMP EWHERE E.DEPT_NO = D.DEPT_NO)20. 识别低效SQL 下列SQL 工具可以识别低效SQL,前提是需要DBA 权限,否则无法进行查询。
SELECT EXECUTIONS, DISK_READS, BUFFER_GETS,ROUND ((BUFFER_GETS-DISK_READS)/BUFFER_GETS, 2) Hit_radio,ROUND (DISK_READS/EXECUTIONS, 2) Reads_per_run,SQL_TEXTFROM V$SQLAREAWHERE EXECUTIONS>0AND BUFFER_GETS > 0AND (BUFFER_GETS-DISK_READS)/BUFFER_GETS < 0.8ORDER BY 4 DESC此外,您还可以使用 SQL Trace 工具收集执行 SQL 的性能状态数据,包括解析次数、执行次数和 CPU 使用时间。
21. SQL with Plan Plan 是一个很好的分析SQL 语句的工具,它甚至可以在不执行SQL 的情况下分析语句。通过分析,我们可以知道如何连接表oracle数据库优化方法,使用如何扫描表(索引扫描或全表扫描)以及使用的索引名称。
22. SQL PLUS 的 TRACE
SQL> listSELECT *FROM dept, empWHERE emp.deptno = dept.deptnoSQL> set autotrace traceonly /*traceonly 可以不显示执行结果*/SQL> /rows selected.Execution Plan----------------------------------------------------------SELECT STATEMENT Optimizer=CHOOSE0 NESTED LOOPS1 TABLE ACCESS (FULL) OF 'EMP'1 TABLE ACCESS (BY INDEX ROWID) OF 'DEPT'3 INDEX (UNIQUE SCAN) OF 'PK_DEPT' (UNIQUE)23. 使用索引提高效率
(1)特点优点:提高效率主键唯一性验证成本:需要空间存储来定期维护和重建索引:
ALTER INDEXREBUILD (2)索引有两种访问方式
(3)基表选择
(4)多个相等的索引
(5)等式比较优先于范围比较有一个非唯一索引,并且也有一个非唯一索引。
SELECT ENAME FROM EMPWHERE DEPTNO > 20AND EMP_CAT = 'A'这里只使用了索引,那么所有的记录都会与条件一一进行比较。执行路径如下:
TABLE ACCESS BY ROWID ON EMPINDEX RANGE SCAN ON CAT_IDX即使对于唯一索引,如果执行范围比较,其优先级也低于非唯一索引的相等比较。
(6)不明确的索引级别当无法判断索引的级别差异时,优化器将只使用一个索引,在WHERE子句中列在最前面。上面有一个非唯一索引,有也是一个非唯一索引。
SELECT ENAME FROM EMPWHERE DEPTNO > 20AND EMP_CAT > 'A'这里只使用索引。执行路径如下:
TABLE ACCESS BY ROWID ON EMPINDEX RANGE SCAN ON DEPT_IDX(7)强制索引无效如果两个或多个索引具有相同的排名,您可以强制优化器使用其中一个(通过它检索的记录更少)。
SELECT ENAMEFROM EMPWHERE EMPNO = 7935AND DEPTNO + 0 = 10 /*DEPTNO上的索引将失效*/AND EMP_TYPE || '' = 'A' /*EMP_TYPE上的索引将失效*/(8)如果索引列是函数的一部分,请避免在索引列上使用计算的 WHERE 子句。优化器将使用全表扫描而不是索引。
/*低效SQL*/SELECT * FROM DEPTWHERE SAL * 12 > 25000;/*高效SQL*/SELECT * FROM DEPTWHERE SAL > 25000/12;(9)如果表中有两个以上(包括两个)索引,则自动选择索引,其中一个是唯一的,另一个是非唯一的。这种情况下,将使用唯一索引而不是完全忽略非唯一索引。
SELECT ENAME FROM EMPWHERE EMPNO = 2326AND DEPTNO = 20;在这里,只有 EMPNO 上的索引是唯一的,所以 EMPNO 索引将用于检索记录。
TABLE ACCESS BY ROWID ON EMPINDEX UNIQUE SCAN ON EMP_NO_IDX(10)避免在索引列上使用NOT 一般来说,我们希望避免在索引列上使用NOT,NOT与在索引列上使用函数的效果相同。当遇到NOT时,它会停止使用index 来执行全表扫描。
/*低效SQL: (这里,不使用索引)*/SELECT * FROM DEPTWHERE NOT DEPT_CODE = 0/*高效SQL: (这里,使用索引)*/SELECT * FROM DEPTWHERE DEPT_CODE > 024. 如果有索引,则将 > 替换为 >=
/*高效SQL*/SELECT * FROM EMPWHERE DEPTNO >=4/*低效SQL*/SELECT * FROM EMPWHERE DEPTNO >3两者的区别在于前者的DBMS会直接跳转到DEPT等于4的第一条记录,而后者会先定位等于3的记录,然后向前扫描到DEPT大于3的第一条记录。
25. 将 OR 替换为 Union(对于索引列) 通常,将 WHERE 子句中的 OR 替换为 UNION 效果会更好。在索引列上使用 OR 将导致全表扫描。请注意,上述规则仅对多个索引列有效。
/*高效SQL*/SELECT LOC_ID , LOC_DESC , REGIONFROM LOCATIONWHERE LOC_ID = 10UNIONSELECT LOC_ID , LOC_DESC , REGIONFROM LOCATIONWHERE REGION = 'MELBOURNE'/*低效SQL*/SELECT LOC_ID,LOC_DESC,REGIONFROM LOCATIONWHERE LOC_ID = 10OR REGION = 'MELBOURNE'26. 用 IN 替换 OR
/*低效SQL*/SELECT * FROM LOCATIONWHERE LOC_ID = 10OR LOC_ID = 20OR LOC_ID = 30/*高效SQL*/SELECT * FROM LOCATIONWHERE LOC_IN IN (10,20,30)实际执行效果还有待测试。下面,两者的执行路径似乎是一样的。
27. 避免在索引列上使用is null 和is not null 避免在索引中使用任何可为空的列,索引将不起作用。
/*低效SQL:(索引失效)*/SELECT * FROM DEPARTMENTWHERE DEPT_CODE IS NOT NULL;/*高效SQL:(索引有效)*/SELECT * FROM DEPARTMENTWHERE DEPT_CODE >=0;28. 始终使用索引的第一列 如果索引建立在多列上,优化器将选择仅在 where 子句引用其第一列 ( ) 时使用该索引。
SQL> create index multindex on multiindexusage(inda,indb);Index created.SQL> select * from multiindexusage where indb = 1;Execution Plan----------------------------------------------------------SELECT STATEMENT Optimizer=CHOOSE0 TABLE ACCESS (FULL) OF 'MULTIINDEXUSAGE‘显然,优化器使用了全表扫描,并在仅引用了索引的第二列时忽略了该索引。
29. 使用UNION ALL代替UNION 当SQL语句需要对两个查询结果集进行UNION时,两个结果集会以UNION-ALL的方式进行合并,然后进行排序后输出最终结果。如果使用 UNION ALL 代替 UNION,则不需要排序,提高效率。由于UNION ALL的结果没有排序oracle数据库优化方法,重复记录也没有过滤,所以是否替换取决于业务需求。
30. UNION的优化 由于UNION对查询结果进行排序,过滤重复记录,所以执行效率不如UNION ALL高。UNION操作会用到一块内存块,所以这块内存的优化也很重要。可以使用以下SQL查询排序的消耗:
select substr(name,1,25) "Sort Area Name",substr(value,1,15) "Value"from v$sysstatwhere name like 'sort%'31. 避免改变索引列的类型
比较不同数据类型的数据时,会自动对列进行简单类型转换。
由于内部发生类型转换,将不会使用此索引。几点注意事项:
32. 使用提示 其他提示 这是一项棘手的工作。建议仅针对特定的少量 SQL 优化提示。
33.几个不能使用索引的WHERE子句(1)在下面的例子中,'!='不会使用索引,索引只能告诉你表中存在什么,不能告诉你什么不存在' t 存在于表中。
/*不使用索引*/SELECT ACCOUNT_NAMEFROM TRANSACTIONWHERE AMOUNT !=0;/*使用索引*/SELECT ACCOUNT_NAMEFROM TRANSACTIONWHERE AMOUNT > 0;(2)在下面的例子中,'||' 是一个字符连接函数。和其他函数一样,索引是禁用的。
/*不使用索引*/SELECT ACCOUNT_NAME,AMOUNTFROM TRANSACTIONWHERE ACCOUNT_NAME||ACCOUNT_TYPE='AMEXA';/*使用索引*/SELECT ACCOUNT_NAME,AMOUNTFROM TRANSACTIONWHERE ACCOUNT_NAME = 'AMEX'AND ACCOUNT_TYPE='A';(3)在下面的示例中,'+' 是一个数学函数。与其他数学函数一样,索引被禁用。
/*不使用索引*/SELECT ACCOUNT_NAME,AMOUNTFROM TRANSACTIONWHERE AMOUNT + 3000 >5000;/*使用索引*/SELECT ACCOUNT_NAME,AMOUNTFROM TRANSACTIONWHERE AMOUNT > 2000 ;(4)在下面的示例中,相同的索引列无法相互比较,这将启用全表扫描。
/*不使用索引*/SELECT ACCOUNT_NAME, AMOUNTFROM TRANSACTIONWHERE ACCOUNT_NAME = NVL(:ACC_NAME, ACCOUNT_NAME)/*使用索引*/SELECT ACCOUNT_NAME,AMOUNTFROM TRANSACTIONWHERE ACCOUNT_NAME LIKE NVL(:ACC_NAME, ’%’)34. 加入多次扫描 如果将列与一组有限的值进行比较,优化器可能会执行多次扫描并合并结果。例子:
SELECT * FROM LODGINGWHERE MANAGER IN ('BILL GATES','KEN MULLER')优化器可能会将其转换为以下形式:
SELECT * FROM LODGINGWHERE MANAGER = 'BILL GATES'OR MANAGER = 'KEN MULLER'35. 在CBO下使用更具选择性的索引
36. 避免资源密集型操作
37. 优化 GROUP BY,通过在 GROUP BY 之前过滤掉不必要的记录来提高 GROUP BY 语句的效率。
/*低效SQL*/SELECT JOB,AVG(SAL)FROM EMPGROUP BY JOBHAVING JOB = 'PRESIDENT''OR JOB = 'MANAGER'/*高效SQL*/SELECT JOB,AVG(SAL)FROM EMPWHERE JOB = 'PRESIDENT'OR JOB = 'MANAGER'GROUP BY JOB38. 使用日期 使用日期时要注意,如果日期加上小数点后5位以上,日期会提前到第二天!
SELECT TO_DATE('01-JAN-93'+.99999)FROM DUAL结果:'01-JAN-93 23:59:59'SELECT TO_DATE('01-JAN-93'+.999999)FROM DUAL结果:'02-JAN-93 00:00:00'39. 使用 show () 来使用隐式游标,它将执行两个操作。第一次检索记录,第二次检查 TOO MANY ROWS 这个。并且显式游标不执行第二个操作。
40. 分离表和索引 好了,关于SQL优化的内容,这篇文章应该可以满足大部分一般的应用优化需求。刚到这里。
直播






