Oracle 数据库查询优化
目录
- 1. Oracle 数据库查询优化(上百万级记录如何提高查询速度)
- 2. Oracle SQL 性能优化 40 条 | 收藏了!
1. Oracle 数据库查询优化(上百万级记录如何提高查询速度)
- 对查询进行优化, 应尽量避免全表扫描, 首先应考虑在
where
及order by
涉及的列上建立索引 - 应尽量避免在
where
子句中对字段进行null
值判断, 否则将导致引擎放弃使用索引而进行全表扫描, 如:
select id from t where num is null
可以在 num 上设置默认值 0, 确保表中 num 列没有 null 值, 然后这样查询:
select id from t where num=0
- 应尽量避免在 where 子句中使用! =或<>操作符, 否则将引擎放弃使用索引而进行全表扫描。
- 应尽量避免在 where 子句中使用 or 来连接条件, 否则将导致引擎放弃使用索引而进行全表扫描, 如:
select id from t where num=10 or num=20
可以这样查询:
select id from t where num=10
union all
select id from t where num=20
- in 和 not in 也要慎用, 否则会导致全表扫描, 如:
select id from t where num in(1,2,3)
对于连续的数值, 能用 between 就不要用 in 了:
select id from t where num between 1 and 3
- 下面的查询也将导致全表扫描:
select id from t where name like '%abc%'
若要提高效率, 可以考虑全文检索。
- 如果在 where 子句中使用参数, 也会导致全表扫描。因为 SQL 只有在运行时才会解析局部变量, 但优化程序不能将访问计划的选择推迟到运行时; 它必须在编译时进行选择。然 而, 如果在编译时建立访问计划, 变量的值还是未知的, 因而无法作为索引选择的输入项。如下面语句将进行全表扫描:
select id from t where num=@num
可以改为强制查询使用索引:
select id from t with(index(索引名)) where num=@num
- 应尽量避免在 where 子句中对字段进行表达式操作, 这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where num/2=100
应改为:
select id from t where num=100*2
- 应尽量避免在 where 子句中对字段进行函数操作, 这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where substring(name,1,3)='abc'--name 以 abc 开头的 id
select id from t where datediff(day,createdate,'2005-11-30')=0--‘2005-11-30’生成的 id
应改为:
select id from t where name like 'abc%'
select id from t where createdate>='2005-11-30' and createdate<'2005-12-1'
- 不要在 where 子句中的"="左边进行函数、算术运算或其他表达式运算, 否则系统将可能无法正确使用索引。
- 在使用索引字段作为条件时, 如果该索引是复合索引, 那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引, 否则该索引将不会被使用, 并且应尽可能的让字段顺序与索引顺序相一致。
- 不要写一些没有意义的查询, 如需要生成一个空表结构:
select col1,col2 into #t from t where 1=0
这类代码不会返回任何结果集, 但是会消耗系统资源的, 应改成这样:
create table #t(...)
- 很多时候用 exists 代替 in 是一个好的选择:
select num from a where num in(select num from b)
用下面的语句替换:
select num from a where exists(select 1 from b where num=a.num)
- 并不是所有索引对查询都有效, SQL 是根据表中数据来进行查询优化的, 当索引列有大量数据重复时, SQL 查询可能不会去利用索引, 如一表中有字段 sex, male、female 几乎各一半, 那么即使在 sex 上建了索引也对查询效率起不了作用。
- 索引并不是越多越好, 索引固然可以提高相应的 select 的效率, 但同时也降低了 insert 及 update 的效率, 因为 insert 或 update 时有可能会重建索引, 所以怎样建索引需要慎重考虑, 视具体情况而定。一个表的索引数最好不要超过 6 个, 若太多则应考虑一些不常使用到的列上建的索引是否有 必要。
- 应尽可能的避免更新 clustered 索引数据列, 因为 clustered 索引数据列的顺序就是表记录的物理存储顺序, 一旦该列值改变将导致整个表记录的顺序的调整, 会耗费相当大的资源。若应用系统需要频繁更新 clustered 索引数据列, 那么需要考虑是否应将该索引建为 clustered 索引。
- 尽量使用数字型字段, 若只含数值信息的字段尽量不要设计为字符型, 这会降低查询和连接的性能, 并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符, 而对于数字型而言只需要比较一次就够了。
- 尽可能的使用 varchar/nvarchar 代替 char/nchar , 因为首先变长字段存储空间小, 可以节省存储空间, 其次对于查询来说, 在一个相对较小的字段内搜索效率显然要高些。
- 任何地方都不要使用 select * from t , 用具体的字段列表代替"*", 不要返回用不到的任何字段。
- 尽量使用表变量来代替临时表。如果表变量包含大量数据, 请注意索引非常有限(只有主键索引)。
- 避免频繁创建和删除临时表, 以减少系统表资源的消耗。
- 临时表并不是不可使用, 适当地使用它们可以使某些例程更有效, 例如, 当需要重复引用大型表或常用表中的某个数据集时。但是, 对于一次性事件, 最好使用导出表。
- 在新建临时表时, 如果一次性插入数据量很大, 那么可以使用 select into 代替 create table, 避免造成大量 log , 以提高速度; 如果数据量不大, 为了缓和系统表的资源, 应先 create table, 然后 insert。
- 如果使用到了临时表, 在存储过程的最后务必将所有的临时表显式删除, 先 truncate table , 然后 drop table , 这样可以避免系统表的较长时间锁定。
- 尽量避免使用游标, 因为游标的效率较差, 如果游标操作的数据超过 1 万行, 那么就应该考虑改写。
- 使用基于游标的方法或临时表方法之前, 应先寻找基于集的解决方案来解决问题, 基于集的方法通常更有效。
- 与临时表一样, 游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于其他逐行处理方法, 尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括"合计"的例程通常要比使用游标执行的速度快。如果开发时 间允许, 基于游标的方法和基于集的方法都可以尝试一下, 看哪一种方法的效果更好。
- 在所有的存储过程和触发器的开始处设置 SET NOCOUNT ON , 在结束时设置 SET NOCOUNT OFF 。无需在执行存储过程和触发器的每个语句后向客户端发送 DONE_IN_PROC 消息。
- 尽量避免大事务操作, 提高系统并发能力。
- 尽量避免向客户端返回大数据量, 若数据量过大, 应该考虑相应需求是否合理。
2. Oracle SQL 性能优化 40 条 | 收藏了!
- SQL 语句执行步骤
语法分析> 语义分析> 视图转换 >表达式转换> 选择优化器 >选择连接方式 >选择连接顺序 >选择数据的搜索路径 >运行"执行计划"
-
选用适合的 Oracle 优化器 RULE(基于规则)、 COST(基于成本) 、CHOOSE(选择性)
-
访问 Table 的方式全表扫描全表扫描就是顺序地访问表中每条记录, ORACLE 采用一次读入多个数据块 (database block) 的方式优化全表扫描。通过 ROWID 访问表 ROWID 包含了表中记录的物理位置信息, ORACLE 采用索引实现了数据和存放数据的物理位置 (ROWID) 之间的联系, 通常索引提供了快速访问 ROWID 的方法, 因此那些基于索引列的查询就可以得到性能上的提高。
-
共享 SQL 语句
- Oracle 提供对执行过的 SQL 语句进行高速缓冲的机制。被解析过并且确定了执行路径的 SQL 语句存放在 SGA 的共享池中。
- Oracle 执行一个 SQL 语句之前每次先从 SGA 共享池中查找是否有缓冲的 SQL 语句, 如果有则直接执行该 SQL 语句。
- 可以通过适当调整 SGA 共享池大小来达到提高 Oracle 执行性能的目的。
- 选择最有效率的表名顺序
- ORACLE 的解析器按照从右到左的顺序处理 FROM 子句中的表名, 因此 FROM 子句中写在最后的表(基础表 driving table) 将被最先处理。
- 当 ORACLE 处理多个表时, 会运用排序及合并的方式连接它们, 并且是从右往左的顺序处理 FROM 子句。首先, 扫描第一个表 (FROM 子句中最后的那个表)并对记录进行排序, 然后扫描第二个表 (FROM 子句中倒数第二个表), 最后将所有从第二个表中检索出的记录与第一个表中合适记录进行合并。
- 只在基于规则的优化器中有效。
举例: 表 TAB1 16,384 条记录表 TAB2 1 条记录
/*选择 TAB2 作为基础表 (最好的方法)*/
SELECT COUNT(*) FROM TAB1,TAB2
/*执行时间 0.96 秒*//*选择 TAB1 作为基础表 (不佳的方法)*/
SELECT COUNT(*) FROM TAB2,TAB1
/*执行时间 26.09 秒*/
如果有 3 个以上的表连接查询, 那就需要选择交叉表 (intersection table) 作为基础表, 交叉表是指那个被其他表所引用的表。
/*高效的 SQL*/
SELECT * FROM LOCATION L, CATEGORY C, EMP E
WHERE E.EMP_NO BETWEEN 1000 AND 2000
AND E.CAT_NO = C.CAT_NO
AND E.LOCN = L.LOCN
将比下列 SQL 更有效率
/*低效的 SQL*/
SELECT * FROM EMP E, LOCATION L, CATEGORY C
WHERE E.CAT_NO = C.CAT_NO
AND E.LOCN = L.LOCN
AND E.EMP_NO BETWEEN 1000 AND 2000
- Where 子句中的连接顺序 Oracle 采用自下而上或自右向左的顺序解析 WHERE 子句。根据这个原理, 表之间的连接必须写在其他 WHERE 条件之前, 那些可以过滤掉最大数量记录的条件必须写在 WHERE 子句的末尾。
/*低效, 执行时间 156.3 秒*/
SELECT Column1,Column2
FROM EMP EWHERE E.SAL > 50000
AND E.JOB = 'MANAGER'
AND 25 <
(SELECT COUNT(*) FROM EMP
WHERE MGR = E.EMPNO)
/*高效, 执行时间 10.6 秒*/
SELECT Column1,Column2FROM EMP E
WHERE 25 < (SELECT COUNT(*) FROM EMP
WHERE MGR=E.EMPNO)
AND E.SAL > 50000
AND E.JOB = 'MANAGER'
- SELECT 子句中避免使用"*"
- Oracle 在解析 SQL 语句的时候, 对于"*"将通过查询数据库字典来将其转换成对应的列名。
- 如果在 Select 子句中需要列出所有的 Column 时, 建议列出所有的 Column 名称, 而不是简单的用"*"来替代, 这样可以减少多于的数据库查询开销。
-
减少访问数据库的次数当执行每条 SQL 语句时, ORACLE 在内部执行了许多工作: 解析 SQL 语句 > 估算索引的利用率 > 绑定变量 > 读数据块等等由此可见, 减少访问数据库的次数 , 就能实际上减少 ORACLE 的工作量。
-
整个简单无关联的数据库访问如果有几个简单的数据库查询语句, 你可以把它们整合到一个查询中(即使它们之间没有关系), 以减少多于的数据库 IO 开销。虽然采取这种方法, 效率得到提高, 但是程序的可读性大大降低, 所以还是要权衡之间的利弊。
-
使用 Truncate 而非 Delete
- Delete 表中记录的时候, Oracle 会在 Rollback 段中保存删除信息以备恢复。Truncate 删除表中记录的时候不保存删除信息, 不能恢复。因此 Truncate 删除记录比 Delete 快, 而且占用资源少。
- 删除表中记录的时候, 如果不需要恢复的情况之下应该尽量使用 Truncate 而不是 Delete。
- Truncate 仅适用于删除全表的记录。
- 尽量多使用 COMMIT 只要有可能, 在程序中尽量多使用 COMMIT, 这样程序的性能得到提高, 需求也会因为 COMMIT 所释放的资源而减少。COMMIT 所释放的资源:
- 回滚段上用于恢复数据的信息。
- 被程序语句获得的锁
- redo log buffer 中的空间
- ORACLE 为管理上述 3 种资源中的内部花费
- 计算记录条数
Select count(*) from tablename;
Select count(1) from tablename;
Select count(column) from tablename;
一般认为, 在没有主键索引的情况之下, 第二种 COUNT(1) 方式最快。如果只有一列且无索引 COUNT(*) 反而比较快, 如果有索引列, 当然是使用索引列 COUNT(column) 最快。
-
用 Where 子句替换 Having 子句避免使用 HAVING 子句, HAVING 只会在检索出所有记录之后才对结果集进行过滤。这个处理需要排序、总计等操作。如果能通过 WHERE 子句限制记录的数目, 就能减少这方面的开销。
-
减少对表的查询操作在含有子查询的 SQL 语句中, 要注意减少对表的查询操作。
/*低效 SQL*/
SELECT TAB_NAME FROM TABLES
WHERE TAB_NAME =(
SELECT TAB_NAME FROM TAB_COLUMNS
WHERE VERSION = 604)
AND DB_VER =(
SELECT DB_VER FROM TAB_COLUMNS
WHERE VERSION = 604)
/*高效 SQL*/
SELECT TAB_NAME FROM TABLES
WHERE (TAB_NAME, DB_VER)=(
SELECT TAB_NAME, DB_VER
FROM TAB_COLUMNS
WHERE VERSION = 604)
-
使用表的别名(Alias)当在 SQL 语句中连接多个表时, 请使用表的别名并把别名前缀于每个 Column 上。这样一来, 就可以减少解析的时间并减少那些由 Column 歧义引起的语法错误。Column 歧义指的是由于 SQL 中不同的表具有相同的 Column 名, 当 SQL 语句中出现这个 Column 时, SQL 解析器无法判断这个 Column 的归属。
-
用 EXISTS 替代 IN 在许多基于基础表的查询中, 为了满足一个条件 , 往往需要对另一个表进行联接。在这种情况下, 使用 EXISTS(或 NOT EXISTS) 通常将提高查询的效率。
/*低效 SQL*/
SELECT * FROM EMP
WHERE EMPNO > 0
AND DEPTNO IN (
SELECT DEPTNO FROM DEPT
WHERE LOC = 'MELB')
/*高效 SQL*/
SELECT * FROM EMP
WHERE EMPNO > 0
AND EXISTS (SELECT 1
FROM DEPT
WHERE DEPT.DEPTNO = EMP.DEPTNO
AND LOC = 'MELB')
- 用 NOT EXISTS 替代 NOT IN 在子查询中, NOT IN 子句将执行一个内部的排序和合并, 对子查询中的表执行一个全表遍历, 因此是非常低效的。为了避免使用 NOT IN, 可以把它改写成外连接(Outer Joins)或者 NOT EXISTS。
/*低效 SQL*/
SELECT * FROM EMP
WHERE DEPT_NO NOT IN (
SELECT DEPT_NO FROM DEPT
WHERE DEPT_CAT='A')
/*高效 SQL*/
SELECT * FROM EMP E
WHERE NOT EXISTS (SELECT 1
FROM DEPT D
WHERE D.DEPT_NO = E.DEPT_NO
AND DEPT_CAT ='A')
- 用表连接替换 EXISTS 通常来说 , 采用表连接的方式比 EXISTS 更有效率 。
/*低效 SQL*/
SELECT ENAME
FROM EMP E
WHERE EXISTS (SELECT 1
FROM DEPT
WHERE DEPT_NO = E.DEPT_NO
AND DEPT_CAT = 'A')
/*高效 SQL*/
SELECT ENAME
FROM DEPT D, EMP E
WHERE E.DEPT_NO = D.DEPT_NO
AND D.DEPT_CAT = 'A'
- 用 EXISTS 替换 DISTINCT 当提交一个包含对多表信息(比如部门表和雇员表)的查询时, 避免在 SELECT 子句中使用 DISTINCT。一般可以考虑用 EXIST 替换。EXISTS 使查询更为迅速, 因为 RDBMS 核心模块将在子查询的条件一旦满足后, 立刻返回结果。
/*低效 SQL*/
SELECT DISTINCT D.DEPT_NO, D.DEPT_NAME
FROM DEPT D, EMP E
WHERE D.DEPT_NO = E.DEPT_NO
/*高效 SQL*/
SELECT D.DEPT_NO, D.DEPT_NAME
FROM DEPT D
WHERE EXISTS (SELECT 1
FROM EMP E
WHERE E.DEPT_NO = D.DEPT_NO)
- 识别低效的 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_TEXT
FROM V$SQLAREA
WHERE EXECUTIONS>0
AND BUFFER_GETS > 0
AND (BUFFER_GETS-DISK_READS)/BUFFER_GETS < 0.8
ORDER BY 4 DESC
另外也可以使用 SQL Trace 工具来收集正在执行的 SQL 的性能状态数据, 包括解析次数, 执行次数, CPU 使用时间等 。
-
用 Explain Plan 分析 SQL 语句 EXPLAIN PLAN 是一个很好的分析 SQL 语句的工具, 它甚至可以在不执行 SQL 的情况下分析语句。通过分析, 我们就可以知道 ORACLE 是怎么样连接表, 使用什么方式扫描表(索引扫描或全表扫描)以及使用到的索引名称。
-
SQL PLUS 的 TRACE
SQL> list
SELECT *
FROM dept, emp
WHERE emp.deptno = dept.deptno
SQL> set autotrace traceonly /*traceonly 可以不显示执行结果*/
SQL> /
rows selected.
Execution Plan
----------------------------------------------------------
SELECT STATEMENT Optimizer=CHOOSE
0 NESTED LOOPS
1 TABLE ACCESS (FULL) OF 'EMP'
1 TABLE ACCESS (BY INDEX ROWID) OF 'DEPT'
3 INDEX (UNIQUE SCAN) OF 'PK_DEPT' (UNIQUE)
- 用索引提高效率
(1)特点优点: 提高效率 主键的唯一性验证代价: 需要空间存储 定期维护重构索引:
LTER INDEX <INDEXNAME> REBUILD <TABLESPACENAME>
(2)Oracle 对索引有两种访问模式
- 索引唯一扫描 (Index Unique Scan)
- 索引范围扫描 (Index Range Scan)
(3)基础表的选择
- 基础表 (Driving Table) 是指被最先访问的表(通常以全表扫描的方式被访问)。根据优化器的不同, SQL 语句中基础表的选择是不一样的。
- 如果你使用的是 CBO (COST BASED OPTIMIZER), 优化器会检查 SQL 语句中的每个表的物理大小, 索引的状态, 然后选用花费最低的执行路径。
- 如果你用 RBO (RULE BASED OPTIMIZER), 并且所有的连接条件都有索引对应, 在这种情况下, 基础表就是 FROM 子句中列在最后的那个表。
(4)多个平等的索引
- 当 SQL 语句的执行路径可以使用分布在多个表上的多个索引时, ORACLE 会同时使用多个索引并在运行时对它们的记录进行合并, 检索出仅对全部索引有效的记录。
- 在 ORACLE 选择执行路径时, 唯一性索引的等级高于非唯一性索引。然而这个规则只有当 WHERE 子句中索引列和常量比较才有效。如果索引列和其他表的索引类相比较。这种子句在优化器中的等级是非常低的。
- 如果不同表中两个相同等级的索引将被引用, FROM 子句中表的顺序将决定哪个会被率先使用。FROM 子句中最后的表的索引将有最高的优先级。
- 如果相同表中两个相同等级的索引将被引用, WHERE 子句中最先被引用的索引将有最高的优先级。
(5)等式比较优先于范围比较 DEPTNO 上有一个非唯一性索引, EMP_CAT 也有一个非唯一性索引。
SELECT ENAME FROM EMP
WHERE DEPTNO > 20
AND EMP_CAT = 'A'
这里只有 EMP_CAT 索引被用到, 然后所有的记录将逐条与 DEPTNO 条件进行比较。执行路径如下:
TABLE ACCESS BY ROWID ON EMP
INDEX RANGE SCAN ON CAT_IDX
即使是唯一性索引, 如果做范围比较, 其优先级也低于非唯一性索引的等式比较。
(6)不明确的索引等级当 ORACLE 无法判断索引的等级高低差别, 优化器将只使用一个索引, 它就是在 WHERE 子句中被列在最前面的。DEPTNO 上有一个非唯一性索引, EMP_CAT 也有一个非唯一性索引。
SELECT ENAME FROM EMP
WHERE DEPTNO > 20
AND EMP_CAT > 'A'
这里, ORACLE 只用到了 DEPT_NO 索引。执行路径如下:
TABLE ACCESS BY ROWID ON EMP
INDEX RANGE SCAN ON DEPT_IDX
(7)强制索引失效如果两个或以上索引具有相同的等级, 你可以强制命令 ORACLE 优化器使用其中的一个(通过它, 检索出的记录数量少) 。
SELECT ENAME
FROM EMP
WHERE EMPNO = 7935
AND DEPTNO + 0 = 10 /*DEPTNO 上的索引将失效*/
AND EMP_TYPE || '' = 'A' /*EMP_TYPE 上的索引将失效*/
(8)避免在索引列上使用计算 WHERE 子句中, 如果索引列是函数的一部分。优化器将不使用索引而使用全表扫描。
/*低效 SQL*/
SELECT * FROM DEPT
WHERE SAL * 12 > 25000;
/*高效 SQL*/
SELECT * FROM DEPT
WHERE SAL > 25000/12;
(9)自动选择索引如果表中有两个以上(包括两个)索引, 其中有一个唯一性索引, 而其他是非唯一性索引。在这种情况下, ORACLE 将使用唯一性索引而完全忽略非唯一性索引。
SELECT ENAME FROM EMP
WHERE EMPNO = 2326
AND DEPTNO = 20;
这里, 只有 EMPNO 上的索引是唯一性的, 所以 EMPNO 索引将用来检索记录。
SELECT ENAME FROM EMP
WHERE EMPNO = 2326
AND DEPTNO = 20;
(10)避免在索引列上使用 NOT 通常, 我们要避免在索引列上使用 NOT, NOT 会产生在和在索引列上使用函数相同的影响。当 ORACLE 遇到 NOT, 它就会停止使用索引转而执行全表扫描。
/*低效 SQL: (这里, 不使用索引)*/
SELECT * FROM DEPT
WHERE NOT DEPT_CODE = 0
/*高效 SQL: (这里, 使用索引)*/
SELECT * FROM DEPT
WHERE DEPT_CODE > 0
- 用 >= 替代 >如果 DEPTNO 上有一个索引
/*高效 SQL*/
SELECT * FROM EMP
WHERE DEPTNO >=4
/*低效 SQL*/
SELECT * FROM EMP
WHERE DEPTNO >3
两者的区别在于, 前者 DBMS 将直接跳到第一个 DEPT 等于 4 的记录, 而后者将首先定位到 DEPTNO 等于 3 的记录并且向前扫描到第一个 DEPT 大于 3 的记录。
- 用 Union 替换 OR(适用于索引列)通常情况下, 用 UNION 替换 WHERE 子句中的 OR 将会起到较好的效果。对索引列使用 OR 将造成全表扫描。注意, 以上规则只针对多个索引列有效。
/*高效 SQL*/
SELECT LOC_ID , LOC_DESC , REGION
FROM LOCATION
WHERE LOC_ID = 10
UNIONS
ELECT LOC_ID , LOC_DESC , REGION
FROM LOCATION
WHERE REGION = 'MELBOURNE'
/*低效 SQL*/
SELECT LOC_ID,LOC_DESC,REGION
FROM LOCATION
WHERE LOC_ID = 10
OR REGION = 'MELBOURNE'
- 用 IN 替换 OR
/*低效 SQL*/
SELECT * FROM LOCATION
WHERE LOC_ID = 10
OR LOC_ID = 20
OR LOC_ID = 30
/*低效 SQL*/
SELECT * FROM LOCATION
WHERE LOC_ID = 10
OR LOC_ID = 20
OR LOC_ID = 30
实际的执行效果还须检验, 在 ORACLE8i 下, 两者的执行路径似乎是相同的。
- 避免在索引列上使用 is null 和 is not null 避免在索引中使用任何可以为空的列, ORACLE 将无法使用该索引。
/*低效 SQL: (索引失效)*/
SELECT * FROM DEPARTMENT
WHERE DEPT_CODE IS NOT NULL;
/*高效 SQL: (索引有效)*/
SELECT * FROM DEPARTMENT
WHERE DEPT_CODE >=0;
- 总是使用索引的第一个列如果索引是建立在多个列上, 只有在它的第一个列 (leading column) 被 where 子句引用时, 优化器才会选择使用该索引。
SQL> create index multindex on multiindexusage(inda,indb);
Index created.SQL> select * from multiindexusage where indb = 1;
Execution Plan
----------------------------------------------------------SELECT STATEMENT Optimizer=CHOOSE
0 TABLE ACCESS (FULL) OF 'MULTIINDEXUSAGE‘
很明显, 当仅引用索引的第二个列时, 优化器使用了全表扫描而忽略了索引。
-
使用 UNION ALL 替代 UNION 当 SQL 语句需要 UNION 两个查询结果集合时, 这两个结果集合会以 UNION-ALL 的方式被合并, 然后在输出最终结果前进行排序。如果用 UNION ALL 替代 UNION, 这样排序就不是必要了, 效率就会因此得到提高。由于 UNION ALL 的结果没有经过排序, 而且不过滤重复的记录, 因此是否进行替换需要根据业务需求而定。
-
对 UNION 的优化由于 UNION 会对查询结果进行排序, 而且过滤重复记录, 因此其执行效率没有 UNION ALL 高。UNION 操作会使用到 SORT_AREA_SIZE 内存块, 因此对这块内存的优化也非常重要。可以使用下面的 SQL 来查询排序的消耗量 :
select substr(name, 1, 25) "Sort Area Name",
substr(value, 1, 15) "Value"
from v$sysstat
where name like 'sort%'
- 避免改变索引列的类型
当比较不同数据类型的数据时, ORACLE 自动对列进行简单的类型转换。
/*假设 EMP_TYPE 是一个字符类型的索引列。*/
SELECT *
FROM EMP
WHERE EMP_TYPE = 123
/*这个语句被 ORACLE 转换为: */
SELECT *
FROM EMP
WHERE TO_NUMBER(EMP_TYPE)=123
因为内部发生的类型转换, 这个索引将不会被用到。几点注意:
- 当比较不同数据类型的数据时, ORACLE 自动对列进行简单的类型转换。
- 如果在索引列上面进行了隐式类型转换, 在查询的时候将不会用到索引。
- 注意当字符和数值比较时, ORACLE 会优先转换数值类型到字符类型。
- 为了避免 ORACLE 对 SQL 进行隐式的类型转换, 最好把类型转换用显式表现出来。
- 使用提示(Hints)
- FULL hint 告诉 ORACLE 使用全表扫描的方式访问指定表。
- ROWID hint 告诉 ORACLE 使用 TABLE ACCESS BY ROWID 的操作访问表。
- CACHE hint 来告诉优化器把查询结果数据保留在 SGA 中。
- INDEX Hint 告诉 ORACLE 使用基于索引的扫描方式。
其他的 Oracle Hints
- ALL_ROWS
- FIRST_ROWS
- RULE
- USE_NL
- USE_MERGE
- USE_HASH 等等。
这是一个很有技巧性的工作。建议只针对特定的, 少数的 SQL 进行 hint 的优化。
- 几种不能使用索引的 WHERE 子句(1)下面的例子中, ‘!=’ 将不使用索引 , 索引只能告诉你什么存在于表中, 而不能告诉你什么不存在于表中。
/*不使用索引*/
SELECT ACCOUNT_NAME
FROM TRANSACTION
WHERE AMOUNT !=0;
/*使用索引*/
SELECT ACCOUNT_NAME
FROM TRANSACTION
WHERE AMOUNT > 0;
(2)下面的例子中, ‘||’是字符连接函数。就象其他函数那样, 停用了索引。
/*不使用索引*/
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';
(3)下面的例子中, ‘+’是数学函数。就象其他数学函数那样, 停用了索引。
/*不使用索引*/
SELECT ACCOUNT_NAME, AMOUNT
FROM TRANSACTION
WHERE AMOUNT + 3000 >5000;
/*使用索引*/
SELECT ACCOUNT_NAME, AMOUNT
FROM TRANSACTION
WHERE AMOUNT > 2000 ;
(4)下面的例子中, 相同的索引列不能互相比较, 这将会启用全表扫描。
/*不使用索引*/
SELECT ACCOUNT_NAME, AMOUNT
FROM TRANSACTION
WHERE ACCOUNT_NAME = NVL(:ACC_NAME, ACCOUNT_NAME)
/*使用索引*/
SELECT ACCOUNT_NAME, AMOUNT
FROM TRANSACTION
WHERE ACCOUNT_NAME LIKE NVL(:ACC_NAME, ’%’)
- 连接多个扫描如果对一个列和一组有限的值进行比较, 优化器可能执行多次扫描并对结果进行合并连接。举例:
SELECT * FROM LODGING
WHERE MANAGER IN ('BILL GATES','KEN MULLER')
优化器可能将它转换成以下形式:
SELECT * FROM LODGING
WHERE MANAGER = 'BILL GATES'
OR MANAGER = 'KEN MULLER'
- CBO 下使用更具选择性的索引
- 基于成本的优化器(CBO, Cost-Based Optimizer)对索引的选择性进行判断来决定索引的使用是否能提高效率。
- 如果检索数据量超过 30%的表中记录数, 使用索引将没有显著的效率提高。
- 在特定情况下, 使用索引也许会比全表扫描慢。而通常情况下, 使用索引比全表扫描要块几倍乃至几千倍!
- 避免使用耗费资源的操作
- 带有 DISTINCT, UNION, MINUS, INTERSECT, ORDER BY 的 SQL 语句会启动 SQL 引擎执行耗费资源的排序(SORT)功能。DISTINCT 需要一次排序操作, 而其他的至少需要执行两次排序。
- 通常, 带有 UNION, MINUS, INTERSECT 的 SQL 语句都可以用其他方式重写。
- 优化 GROUP BY 提高 GROUP BY 语句的效率, 可以通过将不需要的记录在 GROUP BY 之前过滤掉。
/*低效 SQL*/
SELECT JOB,AVG(SAL)FROM EMP
GROUP BY JOB
HAVING JOB = 'PRESIDENT''
OR JOB = 'MANAGER'
/*高效 SQL*/
SELECT JOB,AVG(SAL)FROM EMP
WHERE JOB = 'PRESIDENT'
OR JOB = 'MANAGER'
GROUP BY JOB
- 使用日期当使用日期时, 需要注意如果有超过 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'
-
使用显示游标 (CURSORS) 使用隐式的游标, 将会执行两次操作。第一次检索记录, 第二次检查 TOO MANY ROWS 这个 exception。而显式游标不执行第二次操作。
-
分离表和索引
- 总是将你的表和索引建立在不同的表空间内(TABLESPACES)。
- 决不要将不属于 ORACLE 内部系统的对象存放到 SYSTEM 表空间里。
- 确保数据表空间和索引表空间置于不同的硬盘上。
好了, 关于 Oracle SQL 优化的内容, 这一篇应该满足常规大部分的应用优化要求。就先到这里了。
相关文章:
Oracle 数据库查询优化
目录 1. Oracle 数据库查询优化(上百万级记录如何提高查询速度)2. Oracle SQL 性能优化 40 条 | 收藏了! 1. Oracle 数据库查询优化(上百万级记录如何提高查询速度) 对查询进行优化, 应尽量避免全表扫描, 首先应考虑在 where 及 order by 涉及的列上建立索引应尽量避免在 wher…...
时序预测 | MATLAB实现POA-CNN-GRU鹈鹕算法优化卷积门控循环单元时间序列预测
时序预测 | MATLAB实现POA-CNN-GRU鹈鹕算法优化卷积门控循环单元时间序列预测 目录 时序预测 | MATLAB实现POA-CNN-GRU鹈鹕算法优化卷积门控循环单元时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 MATLAB实现POA-CNN-GRU鹈鹕算法优化卷积门控循环单元时间序…...
Java技术接单
今天给大家介绍一个阶段性(周期性)能获取一定收益的Java技术接单群,分享给大家!主要对搞Java的粉丝有帮助,因为可以赚点小钱,对Java技术的要求不高! 注意:首先进群不是免费的&#…...
多家企业发布基于大模型的AI产品,大模型应用落地哪家强?
https://m.mp.oeeee.com/a/BAAFRD000020230603805161.html “无产业不AI,无应用不AI。” 随着AI(人工智能)大模型技术落地,AI应用遍地开花。连日来,多家企业发布基于大模型的AI应用产品。身处“百模大战”时代&#x…...
如何在小程序中获取用户昵称、电话号,头像
一、如何获取昵称(获取微信昵称)以Taro框架为例 Taro框架中的组件Input的一个属性,type属性的值有一个nickname. 如果要拿到input的值,是要value结合onChange事件。 type"nickname" value{nickName} onChange{(value: …...
26606-2011 工业用氰乙酸甲酯 阅读笔记
声明 本文是学习GB-T 26606-2011 工业用氰乙酸甲酯. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1 范围 本标准规定了工业用氰乙酸甲酯的要求、试验方法、检验规则、标志、包装、运输、贮存和安全。 本标准适用于以氯乙酸、氰化钠、甲醇等为原料…...
微软开源 windows-drivers-rs, 用 Rust 开发 Windows 驱动程序
目录 1. 微软开源 windows-drivers-rs, 用 Rust 开发 Windows 驱动程序 1. 微软开源 windows-drivers-rs, 用 Rust 开发 Windows 驱动程序 Microsoft Azure 首席技术官兼著名 Windows 软件开发人员 Mark Russinovich 在社交平台上宣布, 启动了一个名为 windows-drivers-rs 的新…...
Java中判断字符串是否为合法数字
问题 最近遇到需要将String转BigDecimal的场景。 解决思路 利用NumberUtils.isCreatable判断是否为合法数字,然后,对字符串进行数字转换。注意:这里的NumberUtils类是org.apache.commons.lang3.math库里面的类。 Java if (NumberUtils.i…...
[LeetCode] Hard-2251. 花期内花的数目 - 二分查找/有序数组
Problem: 2251. 花期内花的数目 2251. 花期内花的数目 思路解题方法Code 思路 看题目应该是一道比较经典的差分,本来准备拿差分数组做的,后来搂了一眼题解,发现用二分的方法更简单 解题方法 此题有一种很简便的方法,第i个人到…...
VUE3父子组件传值defineProps() 和 defineEmits()
defineProps 和 defineEmits 都是只能在<script setup>中使用的编译器宏。他们不需要导入,且会随着 <script setup> 的处理过程一同被编译掉。 官网传送门 父组件向子组件传值 defineProps 是 Vue3 中一种新的组件数据传递方式,可以用于在…...
OmniPlan Pro 4 for Mac:引领项目管理的创新与高效
OmniPlan Pro 4是一款强大且高效的项目管理工具,专为Mac用户设计。它提供了一套综合性的解决方案,帮助用户在Mac上便捷地进行项目规划、追踪和管理。凭借其直观的界面,用户可以快速上手,并且能充分利用这款工具的各种功能。 规划…...
封装JDBC,实现简单ORM框架
本文将封装JDBC的操作,实现简单的ORM框架,提供3种风格的api来给用户使用(1.原生jdbcSqlBuilder;2.类似jpa和mp的;3.注解接口方法) 代码仓库:malred/IFullORM 1. 原生JDBCsql构建器 第一步&…...
监控与运维,主流it运维监控工具
IT监管和运行维护已成为企业经营的关键环节。本文将详细介绍IT监管和运行维护的必要性、主要功能和实施策略,帮助企业实现数据安全和高效运行。 IT监管和运行维护的必要性 确保企业数据安全 IT监控系统可以实时监控企业网络、服务器、存储等关键设备的运行情况&…...
基于Matlab实现全局优化算法
Matlab是一种非常强大的数学建模和计算工具,它提供了许多优化算法的实现。全局优化算法是一种能够找到全局最优解的优化算法,相对于局部优化算法来说,具有更强的全局搜索能力。在本文中,我们将介绍如何使用Matlab实现全局优化算法…...
Kafka 笔记 (Non-Root/Container)
目录 1. Kafka 笔记 (Non-Root/Container)1.1. 启动1.2. bitnami/kafka1.2.1. Non-Root Containers 1. Kafka 笔记 (Non-Root/Container) 1.1. 启动 Kafka 需要与 ZooKeeper 一起启动: Kafka with ZooKeeper Run the following commands in order to start all services in…...
【Pytest】跳过执行之@pytest.mark.skip()详解
一、skip介绍及运用 在我们自动化测试过程中,经常会遇到功能阻塞、功能未实现、环境等一系列外部因素问题导致的一些用例执行不了,这时我们就可以用到跳过skip用例,如果我们注释掉或删除掉,后面还要进行恢复操作。 1、skip跳过成…...
Android Framework 常见解决方案(22)防应用被LowMemoryKillerDaemon(LMKD)杀掉
1 原理说明 LMKD 借助 Linux 内核的 OOM(Out of Memory)机制来管理内存。当系统内存不足时,OOM 触发器会发送信号给LMKD,通知其进行内存管理。LMKD根据预先定义的策略和优先级,选择性地终止一些进程,以释放…...
Vue - 组件递归
目录 组件递归子组件父组件 组件递归 当要渲染一个目录时,因为可能有嵌套数据,并且组件的层级未知,可以使用组件递归来解决 注意点: 1,使用递归时必须提供 name,也就是通过组件的 name 递归自己。 2&am…...
微信小程序案例2-1:学生信息
文章目录 (二)准备图像素材(三)编写小程序页面结构 单击[确认] 清空页面结构文件index.wxml内容 修改页面配置文件index.json,不适用navigation-bar组件 删除全局配置文件app.json,删除渲染器配置&a…...
小程序如何设置余额充值
在小程序中设置余额充值是一种非常有效的方式,可以帮助商家吸引更多的会员并提高用户的消费频率。下面将介绍如何在小程序中设置余额充值并使用。 第一步:创建充值方案 在小程序管理员后台->营销管理->余额充值页面,添加充值方案。可…...
vue项目打包成H5apk中使用语音播放
利用浏览器语音播放api功能,在vue项目中调用api实现语音播报。 在mounted生命周期函数中获取浏览器的SpeechSynthesis API data() {return {speech: null,};},mounted() {if ("SpeechSynthesisUtterance" in window) {this.speech window.speechSynthesi…...
windows:批处理bat实例
文章目录 文件/文件夹管理实例批量更改文件名创建编号从0到9的10个文件自动循环运行某个程序显示批处理的完整路径信息将文件名更名为当前系统日期使用批处理命令自动接收用户输入的信息计算当前目录及子目录(中文件)所占硬盘空间自动删除当前目录及子目…...
websocket php教程
WebSocket 是 HTML5 提供的一种网络通讯协议,用于服务端与客户端实时数据传输。广泛用于浏览器与服务器的实时通讯,APP与服务器的实时通讯等场景。 相比传统HTTP协议请求响应式通讯,WebSocket协议可以做到实时的双向通讯,服务端可…...
【操作系统】调度算法的评价指标和三种调度算法
🐌个人主页: 🐌 叶落闲庭 💨我的专栏:💨 c语言 数据结构 javaEE 操作系统 Redis 石可破也,而不可夺坚;丹可磨也,而不可夺赤。 操作系统 一、调度算法的评价指标1.1 CPU利…...
CSS详细基础(三)复合选择器
前两章介绍了CSS中的基础属性,以及一些基础的选择器,本贴开始介绍复合选择器的内容~ 在 CSS 中,可以根据选择器的类型把选择器分为基础选择器和复合选择器,复合选择器是建立在基础选择器之上,对基本选择器进行组合形…...
LeetCode【2251. 花期内花的数目】
给你一个下标从 0 开始的二维整数数组 flowers ,其中 flowers[i] [starti, endi] 表示第 i 朵花的 花期 从 starti 到 endi (都 包含)。同时给你一个下标从 0 开始大小为 n 的整数数组 people ,people[i] 是第 i 个人来看花的时间…...
大数据(九):数据可视化(一)
专栏介绍 结合自身经验和内部资料总结的Python教程,每天3-5章,最短1个月就能全方位的完成Python的学习并进行实战开发,学完了定能成为大佬!加油吧!卷起来! 全部文章请访问专栏:《Python全栈教程(0基础)》 再推荐一下最近热更的:《大厂测试高频面试题详解》 该专栏对…...
讲讲项目里的仪表盘编辑器(二)
应用场景 正常来说,编辑器应用场景应该包括: 编辑器-预览 编辑器 最终运行时 怎么去设计 上一篇推文,我们已经大概了解了编辑器场景。接下来,我们来看预览时的设计 编辑器-预览 点击预览按钮,执行以…...
文心一言 VS 讯飞星火 VS chatgpt (102)-- 算法导论9.3 8题
八、用go语言,设 X[1…n]和 Y[1…n]为两个数组,每个都包含n个有序的元素。请设计一个 O(lgn)时间的算法来找出数组 X和Y中所有 2n 个元素的中位数。 文心一言: 要在 O(lgn) 时间内找出两个有序数组 X 和 Y 中所有元素的中位数,可…...
axios添加缓存请求,防止多次请求,单页面多个同一组件造成多次请求解决方案
在axios中,添加 const cacheMap {};响应拦截添加 try {// 删除缓存const api res.config.url.replace(process.env.VUE_APP_BASE_API, "");if (cacheMap.hasOwnProperty(api)) {delete cacheMap[api];}} catch (err) {}创建两个请求方法 /*** Get缓存…...
云南网站定制/高级seo
原文出处: PAWEL KLIMCZYK 译文出处:码农网/小峰 你已经对着电脑 n 个小时了。敲键盘正成为一种负担,你在想,键盘是否是西西弗斯推着的那块巨石。 伯乐在线转注:西西弗斯是希腊神话中的人物,与更加悲剧…...
动漫网站 设计/百度刷排名seo软件
使用OracleClient 来操作Oracle(Version - 7.3.4.4.0) 发现当遇到字符串内容为中文的时候无法使用parameter 的形式来得到select 的结果。跟踪调试整个过程没有发现任何异常的情况。最后使用Oledb问题解决。个人总结,在Oracle(Version - 8以上版本)可以使用OracleClient没有问题…...
wordpress管理员/seo外包优化网站
数组循环数组循环方法:for循环,forEach(),for/in。一、for循环1.使用Object.keys()参数为对象,返回一个数组,为对象属性名的集合(不可枚举的除外)。IE8,然后使用for循环得到对象的值的集合。 var o{a:1,b:2,c:3}; Obje…...
有趣的网站官网/谷歌seo优化中文章
1、Mybatis优缺点 优点: Mybatis实现了对Dao层的封装,隔离了SQL语句,便于管理,避免了像JDBC那样操作数据集,便于扩展等等。 缺点: Mybatis属于?半自动“ORM”,比Hibernate的工作做得要多很多&a…...
公司做网站多/搜索引擎优化方法有哪些
作者:瀚高PG实验室 (Highgo PG Lab)- 波罗 autovacuum 是 postgresql 里非常重要的一个服务端进程,能够自动地执行,在一定条件下自动地对 dead tuples 进行清理并对表进行分析 autovacuum参数控制 autovacuum 进程是…...
wordpress帮助手册/常用的搜索引擎有哪些?
前言 本文主要记录下关于斯坦福CS231n课程Lecture1——Lecture5中学习的笔记,以下部分内容为个人理解如有错误,敬请原谅。 一、传统机器学习和深度学习联系 不管是传统的机器学习还是深度学习,贯穿主线的就是特征,只不过传统的机…...