- 一 SQL基础知识
- 二 SQL查询
- 三 SQL查询优化
- 尽可能少用 IN 操做符
- 尽可能用 NOT EXISTS 或者外链接替代 NOT IN 操做符
- 尽可能不用或者操做符
- 在设计表时把索引列设置为 NOT NULL
- 尽可能不用通配符或者_做为查询字符串的第一个字符
- Where 子句中避免在索引列上使用计算
- 用替代
- 利用 SGA 共享池避开 parse 阶段
- WHERE 后面的条件顺序要求
- 使用表的别名并将之做为每列的前缀
- 进行了显式或隐式的运算的字段不能进行索引
- 用 UNION ALL 代替 UNION
- 其余操做
- 经过改变 oracle 的 SGA 的大小
- SQL调整最关注的是什么
- 四 ORACLE SQL性能优化
- 五 SQL编程
- 六 oracle数据库
- expimp备份数据库
- 不借助第三方工具怎样查看SQL的执行计划
- 如何使用cbocbo与rule的区别
- 如何定位重要消耗资源多的SQL
- 如何跟踪某个session的SQL
- 如何稳定固定执行计划
- pctused and pctfree 表示什么含义有什么做用
- 简单描述tablespace segment extent block之间的关系
- 描述tablespace和datafile之间的关系
- 本地管理表空间和字典管理表空间的特色assm有什么特色
- 回滚段的做用是什么
- 日志的做用是什么
- sga主要有那些部分主要做用是什么
- oracle系统进程主要有哪些做用是什么
- oracle备份分类
- 归档是什么含义
- 若是一个表在2004-08-04 103000 被drop在有完善的归档和备份的状况下如何恢复
- rman是什么有何特色
- 七 专题研究
- 八 练习题
- 九 存在的问题
- 十 大数据学习
1、 SQL基础知识
数据抽象:物理抽象、概念抽象、视图级抽象,内模式、模式、外模式java
SQL语言包括数据定义、数据操纵(data manipulation),数据控制(data control)mysql
数据定义:create table,alter table,drop table, craete/drop index等ios
数据操纵:select ,insert,update,delete,git
数据控制:grant,revoke程序员
建立、删除数据库
建立:create database database-name正则表达式
删除:drop database dbnameredis
建立、删除修改表
建立表: 算法
综合评价:★★
create table tabname(col1 type1 [not null][primary key],col2 type2 [not null],..)
根据已有的表建立新表:
(1)建一个新表,架构、字段属性、约束条件、数据记录跟旧表彻底同样:
create tabletab_new as select col1,col2…from tab_old;
(2)建一个新表,架构跟旧表彻底同样,但没有内容:
create tabletab_new as select * from tab_old where 1=2;
删除表:
drop table tabname;--删除表结构
只删除表数据:
delete from tabname;(dml操做须要事务提交)
truncate table tabname;(ddl操做当即生效)
修改表:
修改表名:alter table skate_test rename to table_name
添加表注释:comment on table scott. table_name is '注释内容';
添加列注释:comment column on table_name.column_name is '注释内容';
添加、修改、删除列
添加:alter table tablename add (column datatype [defaultvalue][null/not null],….);
修改:alter table tablename modify(column datatype [default value][null/not null],….);
删除:alter table tablename drop(column);
注:列增长后将不能删除。db2中列加上后数据类型也不能改变,惟一能改变的是增长varchar类型的长度。
oracle cascade用法
cascade 关键字主要用于级联,级联删除、级联更新等,综合评价:★★
删除用户:drop user user_name; 删除用户,drop user user_name cascade; 删除此用户名下的全部表和视图
alter table table_name add constraint fk_tn_dept foreign key(dept) references dept(deptno) ([on delete set null],[on delete cascade]); |
添加、删除约束(主键、外键)
一、建立表的同时建立主键约束
(1)无命名
create table student (
studentid int primary key not null,
studentname varchar(8),
ageint);
(2)有命名
create table students (
studentid int ,
studentname varchar(8),
age int,
constraint yy primary key(pk_studentid));
二、向表中添加主键约束
alter table student add constraint pk_studentprimary key(pk_studentid);
三、删除表中已有的主键约束
(1)有命名
alter table students drop constraint yy;
(2)无命名
可用SELECT * FROM user_cons_columns WHEREtable_name = ’ student’
查找表中主键名称得student表中的主键名为SYS_C002715
alter table student drop constraintSYS_C002715;
主键与外键
主键是表格里的(一个或多个)字段,只用来定义表格里的行;主键里的值老是惟一的。
外键是一个用来创建两个表格之间关系的约束。这种关系通常都涉及一个表格里的主键字段与另一个表格(尽管多是同一个表格)里的一系列相连的字段。那么这些相连的字段就是外键。
建立、删除索引
建立:create [unique] index idx_name on tabname(col_name….)
删除:drop index idxname
注:索引是不可更改的,想更改必须删除从新建。
索引做用:
第1、经过建立惟一性索引,能够保证数据库表中每一行数据的惟一性。
第2、能够大大加快数据的检索速度,这也是建立索引的最主要的缘由。
第3、能够加速表和表之间的链接,特别是在实现数据的参考完整性方面特别有意义。
第4、在使用分组和排序子句进行数据检索时,一样能够显著减小查询中分组和排序的时间。
第五, 经过使用索引,能够在查询的过程当中,使用优化隐藏器,提升系统的性能。
索引的做用?和它的优势缺点是什么?
索引就一种特殊的查询表,数据库的搜索引擎能够利用它加速对数据的检索。
优势:它很相似与现实生活中书的目录,不须要查询整本书内容就能够找到想要的数据。索引能够是惟一的,建立索引容许指定单个列或者是多个列。
缺点:是它减慢了数据录入的速度,同时也增长了数据库的尺寸大小。
建立、修改、删除视图
建立、修改:create or replace view view_name as select statement
删除:drop view view_name
2、 SQL查询
基本的SQL语句
选择:select * from table1 where 范围
插入:insert into table1(field1,field2) values(value1,value2)
删除:delete from table1 where 范围
更新:update table1 set field1=value1 where 范围
查找:select * from table1 where field1 like ’%value1%’ ---like语法★★★
排序:select * from table1 order by field1,field2 [desc]
总数:select count(*) as totalcount from table1
求和:select sum(field1) as sumvalue from table1
平均:select avg(field1) as avgvalue from table1
最大:select max(field1) as maxvalue from table1
最小:select min(field1) as minvalue from table1
union、minus、intersect
a:union 运算符
union 运算符经过组合其余两个结果表(例如table1 和 table2)并消去表中任何重复行而派生出一个结果表。当 all 随 union 一块儿使用时(即 union all),不消除重复行。两种状况下,派生表的每一行不是来自 table1 就是来自 table2。
b:minus 运算符
minus 运算符经过包括全部在 table1 中但不在 table2 中的行并消除全部重复行而派生出一个结果表。
c:intersect 运算符
intersect 运算符经过只包括 table1 和 table2 中都有的行并消除全部重复行而派生出一个结果表。
内链接、外链接
内链接,只链接匹配的行
selecta.c1,b.c2 from a join b on a.c3 = b.c3;
左外链接
包含左边表的所有行以及右边表中所有匹配的行
selecta.c1,b.c2 from a left join b on a.c3 = b.c3;
右外链接
包含右边表的所有行以及左边表中所有匹配的行
selecta.c1,b.c2 from a right join b on a.c3 = b.c3;
全外链接
综合评价:★★
包含左、右两个表的所有行
selecta.c1,b.c2 from a full join b on a.c3 = b.c3;
非等链接
使用等值之外的条件来匹配左、右两个表中的行
selecta.c1,b.c2 from a join b on a.c3 != b.c3;
自链接
使用同一张表中的不一样字段进行匹配
select *from t1 a,t1 b where a.a1 = b.a3
交叉链接
生成笛卡尔积——它不使用任何匹配或者选取条件,而是直接将一个数据源中的每一个行与另外一个数据源的每一个行一一匹
配
select a.c1,b.c2 from a,b;
多表关联
select * from a left inner join b ona.a=b.b right inner join c on a.a=c.c inner join d on a.a=d.d where .....
内联接,外联接区别:内链接是保证两个表中全部的行都要知足链接条件,而外链接则否则。
在外链接中,某些不满条件的列也会显示出来,也就是说,只限制其中一个表的行,而不限制另外一个表的行。分左链接、右链接、全链接三种
oracle8i,9i 表链接方法。
通常的相等链接: select * from a, b where a.id = b.id; 这个就属于内链接。
对于外链接:
Oracle中能够使用“(+) ”来表示,9i能够使用left/right/fullouter join
leftouter join:左外关联
selecte.last_name, e.department_id, d.department_name
fromemployees e
leftouter join departments d
on(e.department_id = d.department_id);
等价于
selecte.last_name, e.department_id, d.department_name
fromemployees e, departments d
wheree.department_id=d.department_id(+)
结果为:全部员工及对应部门的记录,包括没有对应部门编号department_id的员工记录。
rightouter join:右外关联
selecte.last_name, e.department_id, d.department_name
fromemployees e
rightouter join departments d
on(e.department_id = d.department_id);
等价于
selecte.last_name, e.department_id, d.department_name
fromemployees e, departments d
wheree.department_id(+)=d.department_id
结果为:全部员工及对应部门的记录,包括没有任何员工的部门记录。
fullouter join:全外关联
selecte.last_name, e.department_id, d.department_name
fromemployees e
fullouter join departments d
on(e.department_id = d.department_id);
结果为:全部员工及对应部门的记录,包括没有对应部门编号department_id的员工记录和没有任何员工的部门记录。
oracle8i是不能在左右两个表上同时加上(+),转换成一个左联接一个右链接
全外链接语法
综合评价: ★★
selectt1.id,t2.id from table1 t1,table t2 where t1.id=t2.id(+)
union
selectt1.id,t2.id from table1 t1,table t2 where t1.id(+)=t2.id
子查询、关联子查询
综合评价:★★★
关联子查询是一种包含子查询的特殊类型的查询。查询里包含的子查询会真正请求外部查询的值,从而造成一个相似于循环的情况。
子查询:
注:表名1:a 表名2:b
select a,b,c from a where a in (select dfrom b) 或者: select a,b,c froma where a in (1,2,3)
关联子查询:
显示文章、提交人和最后回复时间
select a.title,a.username,b.adddate fromtable a, (selectmax(adddate) adddate from table where table.title=a.title) b where b.adddate = a.adddate;——须要显示B表中的字段
scott模式中,找出每一个部门中最高工资的人
select a.deptno, a.* from emp a
where a.sal = (select max(b.sal)from emp b where b.deptno = a.deptno) ;——不须要显示B表中的字段
between、in、exists
综合评价:★★
between 使用方法
between的用法,between限制查询数据范围时包括了边界值,not between不包括
select * from table1 where time between time1 and time2
select a,b,c, from table1 where a not between 数值1 and 数值2
in 的使用方法
select * from table1 where a [not] in (‘值1’,’值2’,’值4’,’值6’)
exists 的使用方法
注:存在两张表,table1 table2
select * from table1 t1 where [not] exists(select * from table2 t2 where t1.id = t2.id)
in、exists使用区别:若是两个表中一个较小,一个是大表,则子查询表大的用exists,子查询表小的用in
复制表(insert into … select、select … into … from)
综合评价:★★
insert into select语句
语句形式为:insert into table2(field1,field2,...) select value1,value2,... from table1
注意:
(1)要求目标表table2必须存在,而且字段field,field2...也必须存在
(2)注意table2的主键约束,若是table2有主键并且不为空,则 field1, field2...中必须包括主键
select into from语句
语句形式为:select vale1, value2 into table2 from table1;
对比:create table tab_new as select col1,col2…from tab_old;
要求目标表table2不存在,由于在插入时会自动建立表table2,并将table1中指定字段数据复制到table2中
3、 SQL查询优化
SQL 优化的实质:在保证结果正确的前提下,充份利用索引,减小表扫描的 I/O 次数,尽可能少访问数据块,尽可能避免全表扫描和其余额外开销。
oracle 经常使用的两种优化器:RBO(rule-based-optimizer)和CBO(cost-based-optimizer)。 目前更多地采用CBO(cost-based-optimizer)基于开销的优化器。在 CBO 方式下,Oracle 会根据表及索引的状态信息来选择计划;在 RBO 方 式下 ,Oracle 会根据本身内部设置的一些 规则来决定选择计划。
一、尽可能少用 IN 操做符
基本上全部的 IN 操做符均可以用EXISTS 代替。IN 和EXISTS 操做的选择要根据主子表数据量大小来具体考虑。
二、尽可能用 NOT EXISTS 或者外链接替代 NOT IN 操做符
由于 NOT IN 不能应用表的索引
三、尽可能不用“<>”或者“!=”操做符
综合评价:★★
不等于操做符是经过全表扫描处理,大于或者小于会使用标的索引。如:a<>0改成 a>0 or a<0
四、在设计表时,把索引列设置为 NOT NULL
判断字段否为空是不会应用索引,由于 B 树索引不会索引空值的。
五、尽可能不用通配符“%”或者“_”做为查询字符串的第一个字符
当通配符“%”或者“_”做为查询字符串的第一个字符时,索引不会被使用 。好比用 T 表中 Column1LIKE ‘%5400%’ 这个条件会产生全表扫描,若是改为 Column1 LIKE ’X5400%’ OR Column1 LIKE ’B5400%’ 则会利用 Column1 的索引进行两个范围的查询。
六、Where 子句中避免在索引列上使用计算
若是索引不是基于函数的,那么当在 Where 子句中对索引列使用函数时索引再也不起做用。好比:substr(no,1,4)=’5400’,优化处理:no like ‘5400%’
trunc(hiredate)=trunc(sysdate) ,优化处理:hiredate >=trunc(sysdate) and hiredate<trunc(sysdate+1)
七、用“>=”替代“>”
综合评价:★★
大于或小于操做符通常状况下是不用调整的,由于它有索引就会采用索引查找,但有的状况下能够对它进行优化,如一个表有 100 万记录,一个数值型字段 A, 30 万记 录的 A=0,30 万记录的 A=1,39 万记录的 A=2,1 万记录的 A=3。那么执行 A>2 与 A>=3 的效果就有很大的区别了,由于 A>2时 ORACLE 会先找出为 2 的记录索引再进行比较,而 A>=3 时 ORACLE 则直接找到=3 的记录索引
八、利用 SGA 共享池,避开 parse 阶段
同一功能同一性能不一样写法 SQL的影响。 如一个 SQL 在
A 程序员写的为:
Select * from zl_yhjbqk;
B 程序员写的为:
Select * from dlyx.zl_yhjbqk(带表全部者的前缀);
C 程序员写的为:
Select * from DLYX.ZLYHJBQK(大写表名);
D 程序员写的为:
Select * from DLYX.ZLYHJBQK(中间多了空格)。
以上四个 SQL 在 ORACLE 分析整理以后产生的结果及执行的时间是同样的,可是从ORACLE 共享内存 SGA 的原理,能够得出 ORACLE 对每一个 SQL 都会对其进行一次分析,而且占用共享内存,若是将 SQL 的字符串及格式写得彻底相同则 ORACLE 只会分析一次,共享内存也只会留下一次的分析结果,这不只能够减小分析 SQL 的时间,并且能够减小共享内存重复的信息,ORACLE 也能够准确统计 SQL 的执行频率。
不一样区域出现的相同的 SQL 语句要保证查询字符彻底相同,建议常用变量来代替常量,以尽可能使用重复 SQL 代码,以利用 SGA 共享池,避开 parse 阶段,防止相同的 SQL 语句被屡次分析,提升执行速度。所以使用存储过程,是一种颇有效的提升 share pool 共享率,跳过 parse 阶段,提升效率的办法。
九、WHERE 后面的条件顺序要求
综合评价:★★
WHERE 后面的条件,表链接语句写在最前,能够过滤掉最大数量记录的条件最后。ORACLE采用自下而上的顺序解析WHERE子句,根据这个原理,表之间的链接必须写在其余WHERE条件以前, 那些能够过滤掉最大数量记录的条件必须写在WHERE子句的末尾。
十、使用表的别名,并将之做为每列的前缀
当在 SQL 语句中链接多个表时,使用表的别名,并将之做为每列的前缀。这样能够减小解析时间
十一、进行了显式或隐式的运算的字段不能进行索引
综合评价:★★
好比:
ss_df+20>50,优化处理:ss_df>30
‘X’||hbs_bh>’X5400021452’,优化处理:hbs_bh>’5400021542’
sk_rq+5=sysdate,优化处理:sk_rq=sysdate-5
hbs_bh=5401002554,优化处理:hbs_bh=’ 5401002554’,注:此条件对hbs_bh 进行隐式的 to_number 转换,由于 hbs_bh 字段是字符型。
十二、用 UNION ALL 代替 UNION
UNION 是最经常使用的集操做,使多个记录集联结成为单个集,对返回的数据行有惟一性要求, 因此 oracle 就须要进行 SORTUNIQUE 操做(与使用 distinct 时操做相似),若是结果集又比较大,则操做会比较慢;
UNION ALL 操做不排除重复记录行,因此会快不少,若是数据自己重复行存在可能性较小时,用 union all 会比用 union 效率高不少!
1三、其余操做
综合评价:★★
尽可能使用 packages: Packages 在第一次调用时能将整个包 load 进内存,对提升性能有帮助。s
尽可能使用 cached sequences来生成 primary key :提升主键生成速度和使用性能。很好地利用空间:如用 VARCHAR2 数据类型代替 CHAR 等
使用 SQL 优化工具:SQLexpert;toad;explain-table;PL/SQL;OEM
1四、经过改变 oracle 的 SGA 的大小
综合评价:★★★
SGA:数据库的系统全局区。
SGA 主要由三部分构成:共享池、数据缓冲区、日志缓冲区
一、 共享池又由两部分构成:共享 SQL 区和数据字典缓冲区。共享SQL 区专门存放用户 SQL 命令,oracle 使用最近最少使用等优先级算法来更新覆盖;数据字典缓冲区(library cache)存放数据库运行的动态信息。数据库运行一段时间后, DBA 须要查看这些内存区域的命中率以从数据库角度对数据库性能调优。经过执行下述语句查看:
select (sum(pins - reloads)) / sum(pins)"Lib Cache" from v$librarycache;
--查看共享 SQL 区的重用率,最好在 90%以上,不然须要增长共享池的大小。 select (sum(gets - getmisses - usage - fixED)) / sum(gets) "Row Cache" fromv$rowcache;
--查看数据字典缓冲区的命中率,最好在 90%以上,不然须要增长共享池的大小。
二、 数据缓冲区:存放 SQL 运行结果抓取到的 datablock;
SELECT name, value FROM v$sysstat WHERE name IN ('db block gets',
'consistent gets','physical reads');
--查看数据库数据缓冲区的使用状况。查询出来的结果能够计算出来数据缓冲区 的使用命中率=1 - (physical reads / (db block gets + consistent gets) )。命中率应该 在 90%以上,不然须要增长数据缓冲区的大小。
三、 日志缓冲区:存放数据库运行生成的日志。
select name,value from v$sysstat where name in ('redo entries','redo log space requests');
--查看日志缓冲区的使用状况。查询出的结果能够计算出日志缓冲区的申请失败
率:申请失败率=requests/entries,申请失败率应该接近于 0,不然说明日志缓冲区开设过小,须要增长 ORACLE 数据库的日志缓冲区。
SQL调整最关注的是什么
检查系统的i/o问题
sar-d能检查整个系统的iostat(iostatistics)
查看该SQL的response time(db block gets/consistentgets/physical reads/sorts (disk))
4、 ORACLE SQL性能优化
1. 选用适合的ORACLE优化器
ORACLE的优化器共有3种:a. RULE (基于规则) b. COST(基于成本) c. CHOOSE (选择性)
设置缺省的优化器,能够经过对init.ora文件中OPTIMIZER_MODE参数的各类声明,如RULE,COST,CHOOSE,ALL_ROWS,FIRST_ROWS. 你固然也在SQL句级或是会话(session)级对其进行覆盖.
为了使用基于成本的优化器(CBO, Cost-Based Optimizer) , 你必须常常运行analyze 命令,以增长数据库中的对象统计信息(object statistics)的准确性.
若是数据库的优化器模式设置为选择性(CHOOSE),那么实际的优化器模式将和是否运行过analyze命令有关. 若是table已经被analyze过, 优化器模式将自动成为CBO , 反之,数据库将采用RULE形式的优化器.
在缺省状况下,ORACLE采用CHOOSE优化器, 为了不那些没必要要的全表扫描(full table scan) , 你必须尽可能避免使用CHOOSE优化器,而直接采用基于规则或者基于成本的优化器.
2. 访问Table的方式
ORACLE 采用两种访问表中记录的方式:
a. 全表扫描
全表扫描就是顺序地访问表中每条记录.ORACLE采用一次读入多个数据块(database block)的方式优化全表扫描.
b. 经过ROWID访问表
你能够采用基于ROWID的访问方式状况,提升访问表的效率, ,ROWID包含了表中记录的物理位置信息..ORACLE采用索引(INDEX)实现了数据和存放数据的物理位置(ROWID)之间的联系. 一般索引提供了快速访问ROWID的方法,所以那些基于索引列的查询就能够获得性能上的提升.
3. 共享SQL语句
为了避免重复解析相同的SQL语句,在第一次解析以后,ORACLE将SQL语句存放在内存中.这块位于系统全局区域SGA(system global area)的共享池(shared bufferpool)中的内存能够被全部的数据库用户共享. 所以,当你执行一个SQL语句(有时被称为一个游标)时,若是它和以前的执行过的语句彻底相同,ORACLE就能很快得到已经被解析的语句以及最好的执行路径. ORACLE的这个功能大大地提升了SQL的执行性能并节省了内存的使用.惋惜的是ORACLE只对简单的表提供高速缓冲(cache buffering) ,这个功能并不适用于多表链接查询.数据库管理员必须在init.ora中为这个区域设置合适的参数,当这个内存区域越大,就能够保留更多的语句,固然被共享的可能性也就越大了.
当你向ORACLE 提交一个SQL语句,ORACLE会首先在这块内存中查找相同的语句.这里须要注明的是,ORACLE对二者采起的是一种严格匹配,要达成共享,SQL语句必须彻底相同(包括空格,换行等).
共享的语句必须知足三个条件:
A. 字符级的比较:当前被执行的语句和共享池中的语句必须彻底相同.
B. 两个语句所指的对象必须彻底相同:
C. 两个SQL语句中必须使用相同的名字的绑定变量(bind variables)
4. 选择最有效率的表名顺序(只在基于规则的优化器中有效)
ORACLE的解析器按照从右到左的顺序处理FROM子句中的表名,所以FROM子句中写在最后的表(基础表 driving table)将被最早处理. 在FROM子句中包含多个表的状况下,你必须选择记录条数最少的表做为基础表.当ORACLE处理多个表时, 会运用排序及合并的方式链接它们.首先,扫描第一个表(FROM子句中最后的那个表)并对记录进行派序,而后扫描第二个表(FROM子句中最后第二个表),最后将全部从第二个表中检索出的记录与第一个表中合适记录进行合并.
例如:
表 TAB1 16,384 条记录
表 TAB2 1 条记录
选择TAB2做为基础表 (最好的方法)
select count(*) from tab1,tab2 执行时间0.96秒
选择TAB2做为基础表 (不佳的方法)
select count(*) from tab2,tab1 执行时间26.09秒
若是有3个以上的表链接查询, 那就须要选择交叉表(intersection table)做为基础表, 交叉表是指那个被其余表所引用的表.
例如:
EMP表描述了LOCATION表和CATEGORY表的交集.
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更有效率
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
5. WHERE子句中的链接顺序.
ORACLE采用自下而上的顺序解析WHERE子句,根据这个原理,表之间的链接必须写在其余WHERE条件以前, 那些能够过滤掉最大数量记录的条件必须写在WHERE子句的末尾.
例如:
(低效,执行时间156.3秒)
SELECT …
FROM EMP E
WHERE SAL > 50000
AND JOB = ‘MANAGER’
AND 25 < (SELECT COUNT(*) FROM EMP
WHERE MGR=E.EMPNO);
(高效,执行时间10.6秒)
SELECT …
FROM EMP E
WHERE 25 < (SELECT COUNT(*) FROM EMP
WHERE MGR=E.EMPNO)
AND SAL > 50000
AND JOB = ‘MANAGER’;
6. SELECT子句中避免使用 ‘ * ‘
当你想在SELECT子句中列出全部的COLUMN时,使用动态SQL列引用 ‘*’ 是一个方便的方法.不幸的是,这是一个很是低效的方法. 实际上,ORACLE在解析的过程当中, 会将’*’ 依次转换成全部的列名, 这个工做是经过查询数据字典完成的, 这意味着将耗费更多的时间.
7. 减小访问数据库的次数
当执行每条SQL语句时, ORACLE在内部执行了许多工做: 解析SQL语句, 估算索引的利用率, 绑定变量 , 读数据块等等. 因而可知, 减小访问数据库的次数 , 就能实际上减小ORACLE的工做量.
例如,
如下有三种方法能够检索出雇员号等于0342或0291的职员.
方法1 (最低效)
SELECT EMP_NAME , SALARY , GRADE
FROM EMP
WHERE EMP_NO = 342;
SELECT EMP_NAME , SALARY , GRADE
FROM EMP
WHERE EMP_NO = 291;
方法2 (次低效)
DECLARE
CURSOR C1 (E_NO NUMBER) IS
SELECT EMP_NAME,SALARY,GRADE
FROM EMP
WHERE EMP_NO = E_NO;
BEGIN
OPEN C1(342);
FETCH C1 INTO …,..,.. ;
…..
OPEN C1(291);
FETCH C1 INTO …,..,.. ;
CLOSE C1;
END;
方法3 (高效)
SELECT A.EMP_NAME , A.SALARY ,A.GRADE,
B.EMP_NAME , B.SALARY , B.GRADE
FROM EMP A,EMP B
WHERE A.EMP_NO = 342
AND B.EMP_NO = 291;
注意:
在SQL*Plus , SQL*Forms和Pro*C中从新设置ARRAYSIZE参数, 能够增长每次数据库访问的检索数据量 ,建议值为200
ORACLE SQL性能优化系列 (三)
8. 使用DECODE函数来减小处理时间
使用DECODE函数能够避免重复扫描相同记录或重复链接相同的表.
例如:
SELECT COUNT(*),SUM(SAL)
FROM EMP
WHERE DEPT_NO = 0020
AND ENAME LIKE ‘SMITH%’;
SELECT COUNT(*),SUM(SAL)
FROM EMP
WHERE DEPT_NO = 0030
AND ENAME LIKE ‘SMITH%’;
你能够用DECODE函数高效地获得相同结果
SELECT COUNT(DECODE(DEPT_NO,0020,’X’,NULL)) D0020_COUNT,
COUNT(DECODE(DEPT_NO,0030,’X’,NULL)) D0030_COUNT,
SUM(DECODE(DEPT_NO,0020,SAL,NULL))D0020_SAL,
SUM(DECODE(DEPT_NO,0030,SAL,NULL))D0030_SAL
FROM EMP WHERE ENAME LIKE ‘SMITH%’;
相似的,DECODE函数也能够运用于GROUP BY 和ORDER BY子句中.
9. 整合简单,无关联的数据库访问
若是你有几个简单的数据库查询语句,你能够把它们整合到一个查询中(即便它们之间没有关系)
例如:
SELECT NAME
FROM EMP
WHERE EMP_NO = 1234;
SELECT NAME
FROM DPT
WHERE DPT_NO = 10 ;
SELECT NAME
FROM CAT
WHERE CAT_TYPE = ‘RD’;
上面的3个查询能够被合并成一个:
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’;
(译者按: 虽然采起这种方法,效率获得提升,可是程序的可读性大大下降,因此读者仍是要权衡之间的利弊)
10. 删除重复记录
最高效的删除重复记录方法 ( 由于使用了ROWID)
DELETE FROM EMP E
WHERE E.ROWID > (SELECT MIN(X.ROWID)
FROM EMP X
WHERE X.EMP_NO = E.EMP_NO);
12. 尽可能多使用COMMIT
只要有可能,在程序中尽可能多使用COMMIT, 这样程序的性能获得提升,需求也会由于COMMIT所释放的资源而减小:
COMMIT所释放的资源:
a. 回滚段上用于恢复数据的信息.
b. 被程序语句得到的锁
c. redo log buffer 中的空间
d. ORACLE为管理上述3种资源中的内部花费
(译者按: 在使用COMMIT时必需要注意到事务的完整性,现实中效率和事务完整性每每是鱼和熊掌不可得兼)
ORACLE SQL性能优化系列 (四)
13. 计算记录条数
和通常的观点相反, count(*) 比count(1)稍快 , 固然若是能够经过索引检索,对索引列的计数仍旧是最快的. 例如 COUNT(EMPNO)
(译者按: 在CSDN论坛中,曾经对此有过至关热烈的讨论, 做者的观点并不十分准确,经过实际的测试,上述三种方法并无显著的性能差异)
14. 用Where子句替换HAVING子句
避免使用HAVING子句, HAVING 只会在检索出全部记录以后才对结果集进行过滤. 这个处理须要排序,总计等操做.若是能经过WHERE子句限制记录的数目,那就能减小这方面的开销.
例如:
低效:
SELECT REGION,AVG(LOG_SIZE)
FROM LOCATION
GROUP BY REGION
HAVING REGION REGION != ‘SYDNEY’
AND REGION != ‘PERTH’
高效
SELECT REGION,AVG(LOG_SIZE)
FROM LOCATION
WHERE REGION REGION != ‘SYDNEY’
AND REGION != ‘PERTH’
GROUP BY REGION
(译者按:HAVING 中的条件通常用于对一些集合函数的比较,如COUNT() 等等. 除此而外,通常的条件应该写在WHERE子句中)
15. 减小对表的查询
在含有子查询的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)
高效
SELECT TAB_NAME
FROM TABLES
WHERE (TAB_NAME,DB_VER)
= ( SELECT TAB_NAME,DB_VER)
FROM TAB_COLUMNS
WHERE VERSION = 604)
Update 多个Column 例子:
低效:
UPDATE EMP
SET EMP_CAT = (SELECT MAX(CATEGORY)FROM EMP_CATEGORIES),
SAL_RANGE = (SELECT MAX(SAL_RANGE) FROMEMP_CATEGORIES)
WHERE EMP_DEPT = 0020;
高效:
UPDATE EMP
SET (EMP_CAT, SAL_RANGE)
= (SELECT MAX(CATEGORY) ,MAX(SAL_RANGE)
FROM EMP_CATEGORIES)
WHERE EMP_DEPT = 0020;
16. 经过内部函数提升SQL效率.
SELECTH.EMPNO,E.ENAME,H.HIST_TYPE,T.TYPE_DESC,COUNT(*)
FROM HISTORY_TYPE T,EMP E,EMP_HISTORY H
WHERE H.EMPNO = E.EMPNO
AND H.HIST_TYPE = T.HIST_TYPE
GROUP BYH.EMPNO,E.ENAME,H.HIST_TYPE,T.TYPE_DESC;
经过调用下面的函数能够提升效率.
FUNCTION LOOKUP_HIST_TYPE(TYP INNUMBER) RETURN VARCHAR2
AS
TDESC VARCHAR2(30);
CURSOR C1 IS
SELECT TYPE_DESC
FROM HISTORY_TYPE
WHERE HIST_TYPE = TYP;
BEGIN
OPEN C1;
FETCH C1 INTO TDESC;
CLOSE C1;
RETURN (NVL(TDESC,’?’));
END;
FUNCTION LOOKUP_EMP(EMP IN NUMBER)RETURN VARCHAR2
AS
ENAME VARCHAR2(30);
CURSOR C1 IS
SELECT ENAME
FROM EMP
WHERE EMPNO=EMP;
BEGIN
OPEN C1;
FETCH C1 INTO ENAME;
CLOSE C1;
RETURN (NVL(ENAME,’?’));
END;
SELECT H.EMPNO,LOOKUP_EMP(H.EMPNO),
H.HIST_TYPE,LOOKUP_HIST_TYPE(H.HIST_TYPE),COUNT(*)
FROM EMP_HISTORY H
GROUP BY H.EMPNO , H.HIST_TYPE;
ORACLE SQL性能优化系列 (六)
20. 用表链接替换EXISTS
一般来讲 , 采用表链接的方式比EXISTS更有效率
SELECT ENAME
FROM EMP E
WHERE EXISTS (SELECT ‘X’
FROM DEPT
WHERE DEPT_NO = E.DEPT_NO
AND DEPT_CAT = ‘A’);
(更高效)
SELECT ENAME
FROM DEPT D,EMP E
WHERE E.DEPT_NO = D.DEPT_NO
AND DEPT_CAT = ‘A’ ;
21. 用EXISTS替换DISTINCT
当提交一个包含一对多表信息(好比部门表和雇员表)的查询时,避免在SELECT子句中使用DISTINCT. 通常能够考虑用EXIST替换
例如:
低效:
SELECT DISTINCT DEPT_NO,DEPT_NAME
FROM DEPT D,EMP E
WHERE D.DEPT_NO = E.DEPT_NO
高效:
SELECT DEPT_NO,DEPT_NAME
FROM DEPT D
WHERE EXISTS ( SELECT ‘X’
FROM EMP E
WHERE E.DEPT_NO = D.DEPT_NO);
EXISTS 使查询更为迅速,由于RDBMS核心模块将在子查询的条件一旦知足后,马上返回结果.
22. 识别’低效执行’的SQL语句
用下列SQL工具找出低效SQL:
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优化的图形化工具层出不穷,可是写出本身的SQL工具来解决问题始终是一个最好的方法)
23. 使用TKPROF 工具来查询SQL性能状态
SQL trace 工具收集正在执行的SQL的性能状态数据并记录到一个跟踪文件中. 这个跟踪文件提供了许多有用的信息,例如解析次数.执行次数,CPU使用时间等.这些数据将能够用来优化你的系统.
设置SQL TRACE在会话级别: 有效
ALTER SESSION SET SQL_TRACE TRUE
设置SQL TRACE 在整个数据库有效仿, 你必须将SQL_TRACE参数在init.ora中设为TRUE, USER_DUMP_DEST参数说明了生成跟踪文件的目录
ORACLE SQL性能优化系列 (七 )
24. 用EXPLAIN PLAN 分析SQL语句
EXPLAIN PLAN 是一个很好的分析SQL语句的工具,它甚至能够在不执行SQL的状况下分析语句. 经过分析,咱们就能够知道ORACLE是怎么样链接表,使用什么方式扫描表(索引扫描或全表扫描)以及使用到的索引名称.
你须要按照从里到外,从上到下的次序解读分析的结果. EXPLAIN PLAN分析的结果是用缩进的格式排列的, 最内部的操做将被最早解读, 若是两个操做处于同一层中,带有最小操做号的将被首先执行.
NESTED LOOP是少数不按照上述规则处理的操做, 正确的执行路径是检查对NESTED LOOP提供数据的操做,其中操做号最小的将被最早处理.
译者按:
经过实践, 感到仍是用SQLPLUS中的SET TRACE 功能比较方便.
举例:
SQL> list
1 SELECT *
2 FROM dept, emp
3* WHERE emp.deptno = dept.deptno
SQL> set autotrace on exp;/*traceonly 能够不显示执行结果*/
或者SQL> set autotrace traceonly exp;
SQL> /
14 rows selected.
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 NESTED LOOPS
2 1 TABLE ACCESS (FULL) OF 'EMP'
3 1 TABLE ACCESS (BY INDEX ROWID) OF'DEPT'
4 3 INDEX (UNIQUE SCAN) OF 'PK_DEPT'(UNIQUE)
Statistics
----------------------------------------------------------
0 recursive calls
2 db block gets
30 consistent gets
0 physical reads
0 redo size
2598 bytes sent via SQL*Net to client
503 bytes received via SQL*Net fromclient
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
14 rows processed
经过以上分析,能够得出实际的执行步骤是:
1. TABLE ACCESS (FULL) OF 'EMP'
2. INDEX (UNIQUE SCAN) OF 'PK_DEPT'(UNIQUE)
3. TABLE ACCESS (BY INDEX ROWID) OF'DEPT'
4. NESTED LOOPS (JOINING 1 AND 3)
ORACLE SQL性能优化系列 (八)
25. 用索引提升效率
索引是表的一个概念部分,用来提升检索数据的效率. 实际上,ORACLE使用了一个复杂的自平衡B-tree结构. 一般,经过索引查询数据比全表扫描要快. 当ORACLE找出执行查询和Update语句的最佳路径时, ORACLE优化器将使用索引. 一样在联结多个表时使用索引也能够提升效率. 另外一个使用索引的好处是,它提供了主键(primary key)的惟一性验证.
除了那些LONG或LONG RAW数据类型, 你能够索引几乎全部的列. 一般, 在大型表中使用索引特别有效. 固然,你也会发现, 在扫描小表时,使用索引一样能提升效率.
虽然使用索引能获得查询效率的提升,可是咱们也必须注意到它的代价. 索引须要空间来
存储,也须要按期维护, 每当有记录在表中增减或索引列被修改时, 索引自己也会被修改. 这意味着每条记录的INSERT , DELETE , UPDATE将为此多付出4 , 5 次的磁盘I/O . 由于索引须要额外的存储空间和处理,那些没必要要的索引反而会使查询反应时间变慢.
译者按:
按期的重构索引是有必要的.
ALTER INDEX <INDEXNAME> REBUILD<TABLESPACENAME>
26. 索引的操做
ORACLE对索引有两种访问模式.
索引惟一扫描 ( INDEX UNIQUE SCAN)
大多数状况下, 优化器经过WHERE子句访问INDEX.
例如:
表LODGING有两个索引 : 创建在LODGING列上的惟一性索引LODGING_PK和创建在MANAGER列上的非惟一性索引LODGING$MANAGER.
SELECT *
FROM LODGING
WHERE LODGING = ‘ROSE HILL’;
在内部 , 上述SQL将被分红两步执行, 首先 , LODGING_PK 索引将经过索引惟一扫描的方式被访问 , 得到相对应的ROWID, 经过ROWID访问表的方式执行下一步检索.
若是被检索返回的列包括在INDEX列中,ORACLE将不执行第二步的处理(经过ROWID访问表). 由于检索数据保存在索引中, 单单访问索引就能够彻底知足查询结果.
下面SQL只须要INDEXUNIQUE SCAN 操做.
SELECT LODGING
FROM LODGING
WHERE LODGING = ‘ROSE HILL’;
索引范围查询(INDEX RANGE SCAN)
适用于两种状况:
1. 基于一个范围的检索
2. 基于非惟一性索引的检索
例1:
SELECT LODGING
FROM LODGING
WHERE LODGING LIKE ‘M%’;
WHERE子句条件包括一系列值, ORACLE将经过索引范围查询的方式查询LODGING_PK . 因为索引范围查询将返回一组值, 它的效率就要比索引惟一扫描低一些.
例2:
SELECT LODGING
FROM LODGING
WHERE MANAGER = ‘BILL GATES’;
这个SQL的执行分两步,LODGING$MANAGER的索引范围查询(获得全部符合条件记录的ROWID) 和下一步同过ROWID访问表获得LODGING列的值. 因为LODGING$MANAGER是一个非惟一性的索引,数据库不能对它执行索引惟一扫描.
因为SQL返回LODGING列,而它并不存在于LODGING$MANAGER索引中, 因此在索引范围查询后会执行一个经过ROWID访问表的操做.
WHERE子句中, 若是索引列所对应的值的第一个字符由通配符(WILDCARD)开始, 索引将不被采用.
SELECT LODGING
FROM LODGING
WHERE MANAGER LIKE ‘%HANMAN’;
在这种状况下,ORACLE将使用全表扫描.
ORACLE SQL性能优化系列 (九)
27. 基础表的选择
基础表(Driving Table)是指被最早访问的表(一般以全表扫描的方式被访问). 根据优化器的不一样, SQL语句中基础表的选择是不同的.
若是你使用的是CBO (COST BASED OPTIMIZER),优化器会检查SQL语句中的每一个表的物理大小,索引的状态,而后选用花费最低的执行路径.
若是你用RBO (RULE BASED OPTIMIZER) , 而且全部的链接条件都有索引对应, 在这种状况下, 基础表就是FROM 子句中列在最后的那个表.
举例:
SELECT A.NAME , B.MANAGER
FROM WORKERA,
LODGING B
WHERE A.LODGING = B.LODING;
因为LODGING表的LODING列上有一个索引, 并且WORKER表中没有相比较的索引, WORKER表将被做为查询中的基础表.
28. 多个平等的索引
当SQL语句的执行路径能够使用分布在多个表上的多个索引时, ORACLE会同时使用多个索引并在运行时对它们的记录进行合并, 检索出仅对所有索引有效的记录.
在ORACLE选择执行路径时,惟一性索引的等级高于非惟一性索引. 然而这个规则只有
当WHERE子句中索引列和常量比较才有效.若是索引列和其余表的索引类相比较. 这种子句在优化器中的等级是很是低的.
若是不一样表中两个想同等级的索引将被引用, FROM子句中表的顺序将决定哪一个会被率先使用. FROM子句中最后的表的索引将有最高的优先级.
若是相同表中两个想同等级的索引将被引用, WHERE子句中最早被引用的索引将有最高的优先级.
举例:
DEPTNO上有一个非惟一性索引,EMP_CAT也有一个非惟一性索引.
SELECT ENAME,
FROM EMP
WHERE DEPT_NO = 20
AND EMP_CAT = ‘A’;
这里,DEPTNO索引将被最早检索,而后同EMP_CAT索引检索出的记录进行合并. 执行路径以下:
TABLE ACCESS BY ROWID ON EMP
AND-EQUAL
INDEX RANGE SCAN ON DEPT_IDX
INDEX RANGE SCAN ON CAT_IDX
29. 等式比较和范围比较
当WHERE子句中有索引列,ORACLE不能合并它们,ORACLE将用范围比较.
举例:
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
30. 不明确的索引等级
当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
译者按:
咱们来试一下如下这种状况:
SQL> select index_name, uniquenessfrom user_indexes where table_name = 'EMP';
INDEX_NAME UNIQUENES
---------------------------------------
EMPNO UNIQUE
EMPTYPE NONUNIQUE
SQL> select * from emp where empno>= 2 and emp_type = 'A' ;
no rows selected
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (BY INDEX ROWID) OF'EMP'
2 1 INDEX (RANGE SCAN) OF 'EMPTYPE'(NON-UNIQUE)
虽然EMPNO是惟一性索引,可是因为它所作的是范围比较, 等级要比非惟一性索引的等式比较低!
ORACLE SQL性能优化系列 (十)
31. 强制索引失效
若是两个或以上索引具备相同的等级,你能够强制命令ORACLE优化器使用其中的一个(经过它,检索出的记录数量少) .
举例:
SELECT ENAME
FROM EMP
WHERE EMPNO = 7935
AND DEPTNO + 0 = 10 /*DEPTNO上的索引将失效*/
AND EMP_TYPE || ‘’ = ‘A’ /*EMP_TYPE上的索引将失效*/
这是一种至关直接的提升查询效率的办法. 可是你必须谨慎考虑这种策略,通常来讲,只有在你但愿单独优化几个SQL时才能采用它.
这里有一个例子关于什么时候采用这种策略,
假设在EMP表的EMP_TYPE列上有一个非惟一性的索引而EMP_CLASS上没有索引.
SELECT ENAME
FROM EMP
WHERE EMP_TYPE = ‘A’
AND EMP_CLASS = ‘X’;
优化器会注意到EMP_TYPE上的索引并使用它. 这是目前惟一的选择. 若是,一段时间之后, 另外一个非惟一性创建在EMP_CLASS上,优化器必须对两个索引进行选择,在一般状况下,优化器将使用两个索引并在他们的结果集合上执行排序及合并. 然而,若是其中一个索引(EMP_TYPE)接近于惟一性而另外一个索引(EMP_CLASS)上有几千个重复的值. 排序及合并就会成为一种没必要要的负担. 在这种状况下,你但愿使优化器屏蔽掉EMP_CLASS索引.
用下面的方案就能够解决问题.
SELECT ENAME
FROM EMP
WHERE EMP_TYPE = ‘A’
AND EMP_CLASS||’’ = ‘X’;
32. 避免在索引列上使用计算.
WHERE子句中,若是索引列是函数的一部分.优化器将不使用索引而使用全表扫描.
举例:
低效:
SELECT …
FROM DEPT
WHERE SAL * 12 > 25000;
高效:
SELECT …
FROM DEPT
WHERE SAL > 25000/12;
译者按:
这是一个很是实用的规则,请务必牢记
33. 自动选择索引
若是表中有两个以上(包括两个)索引,其中有一个惟一性索引,而其余是非惟一性.
在这种状况下,ORACLE将使用惟一性索引而彻底忽略非惟一性索引.
举例:
SELECT ENAME
FROM EMP
WHERE EMPNO = 2326
AND DEPTNO = 20 ;
这里,只有EMPNO上的索引是惟一性的,因此EMPNO索引将用来检索记录.
TABLE ACCESS BY ROWID ON EMP
INDEX UNIQUE SCAN ON EMP_NO_IDX
34. 避免在索引列上使用NOT
一般, 咱们要避免在索引列上使用NOT, NOT会产生在和在索引列上使用函数相同的
影响. 当ORACLE”遇到”NOT,他就会中止使用索引转而执行全表扫描.
举例:
低效: (这里,不使用索引)
SELECT …
FROM DEPT
WHERE DEPT_CODE NOT = 0;
高效: (这里,使用了索引)
SELECT …
FROM DEPT
WHERE DEPT_CODE > 0;
须要注意的是,在某些时候,ORACLE优化器会自动将NOT转化成相对应的关系操做符.
NOT > to <=
NOT >= to <
NOT < to >=
NOT <= to >
译者按:
在这个例子中,做者犯了一些错误. 例子中的低效率SQL是不能被执行的.
我作了一些测试:
SQL> select * from emp where NOTempno > 1;
no rows selected
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (BY INDEX ROWID) OF'EMP'
2 1 INDEX (RANGE SCAN) OF 'EMPNO'(UNIQUE)
SQL> select * from emp where empno<= 1;
no rows selected
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (BY INDEX ROWID) OF'EMP'
2 1 INDEX (RANGE SCAN) OF 'EMPNO'(UNIQUE)
二者的效率彻底同样,也许这符合做者关于” 在某些时候, ORACLE优化器会自动将NOT转化成相对应的关系操做符” 的观点.
35. 用>=替代>
若是DEPTNO上有一个索引,
高效:
SELECT *
FROM EMP
WHERE DEPTNO >=4
低效:
SELECT *
FROM EMP
WHERE DEPTNO >3
二者的区别在于, 前者DBMS将直接跳到第一个DEPT等于4的记录然后者将首先定位到DEPTNO=3的记录而且向前扫描到第一个DEPT大于3的记录.
ORACLE SQL性能优化系列 (十一)
36. 用UNION替换OR (适用于索引列)
一般状况下, 用UNION替换WHERE子句中的OR将会起到较好的效果. 对索引列使用OR将形成全表扫描. 注意, 以上规则只针对多个索引列有效. 若是有column没有被索引, 查询效率可能会由于你没有选择OR而下降.
在下面的例子中, LOC_ID 和REGION上都建有索引.
高效:
SELECT LOC_ID , LOC_DESC , REGION
FROM LOCATION
WHERE LOC_ID = 10
UNION
SELECT LOC_ID , LOC_DESC , REGION
FROM LOCATION
WHERE REGION = “MELBOURNE”
低效:
SELECT LOC_ID , LOC_DESC , REGION
FROM LOCATION
WHERE LOC_ID = 10 OR REGION = “MELBOURNE”
若是你坚持要用OR, 那就须要返回记录最少的索引列写在最前面.
注意:
WHERE KEY1 = 10 (返回最少记录)
OR KEY2 = 20 (返回最多记录)
ORACLE 内部将以上转换为
WHERE KEY1 = 10 AND
((NOT KEY1 = 10) AND KEY2 = 20)
译者按:
下面的测试数据仅供参考: (a = 1003 返回一条记录 , b = 1 返回1003条记录)
SQL> select * from unionvsor /*1sttest*/
2 where a = 1003 or b = 1;
1003 rows selected.
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 CONCATENATION
2 1 TABLE ACCESS (BY INDEX ROWID) OF'UNIONVSOR'
3 2 INDEX (RANGE SCAN) OF 'UB'(NON-UNIQUE)
4 1 TABLE ACCESS (BY INDEX ROWID) OF'UNIONVSOR'
5 4 INDEX (RANGE SCAN) OF 'UA'(NON-UNIQUE)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
144 consistent gets
0 physical reads
0 redo size
63749 bytes sent via SQL*Net to client
7751 bytes received via SQL*Net fromclient
68 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1003 rows processed
SQL> select * from unionvsor /*2ndtest*/
2 where b = 1 or a = 1003 ;
1003 rows selected.
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 CONCATENATION
2 1 TABLE ACCESS (BY INDEX ROWID) OF'UNIONVSOR'
3 2 INDEX (RANGE SCAN) OF 'UA'(NON-UNIQUE)
4 1 TABLE ACCESS (BY INDEX ROWID) OF'UNIONVSOR'
5 4 INDEX (RANGE SCAN) OF 'UB'(NON-UNIQUE)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
143 consistent gets
0 physical reads
0 redo size
63749 bytes sent via SQL*Net to client
7751 bytes received via SQL*Net fromclient
68 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1003 rows processed
SQL> select * from unionvsor /*3rdtest*/
2 where a = 1003
3 union
4 select * from unionvsor
5 where b = 1;
1003 rows selected.
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 SORT (UNIQUE)
2 1 UNION-ALL
3 2 TABLE ACCESS (BY INDEX ROWID) OF'UNIONVSOR'
4 3 INDEX (RANGE SCAN) OF 'UA'(NON-UNIQUE)
5 2 TABLE ACCESS (BY INDEX ROWID) OF'UNIONVSOR'
6 5 INDEX (RANGE SCAN) OF 'UB'(NON-UNIQUE)
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
10 consistent gets
0 physical reads
0 redo size
63735 bytes sent via SQL*Net to client
7751 bytes received via SQL*Net fromclient
68 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
1003 rows processed
用UNION的效果能够从consistentgets和 SQL*NET的数据交换量的减小看出
37. 用IN来替换OR
下面的查询能够被更有效率的语句替换:
低效:
SELECT….
FROM LOCATION
WHERE LOC_ID = 10
OR LOC_ID = 20
OR LOC_ID = 30
高效
SELECT…
FROM LOCATION
WHERE LOC_IN IN (10,20,30);
译者按:
这是一条简单易记的规则,可是实际的执行效果还须检验,在ORACLE8i下,二者的执行路径彷佛是相同的.
38. 避免在索引列上使用IS NULL和IS NOT NULL
避免在索引中使用任何能够为空的列,ORACLE将没法使用该索引.对于单列索引,若是列包含空值,索引中将不存在此记录. 对于复合索引,若是每一个列都为空,索引中一样不存在此记录. 若是至少有一个列不为空,则记录存在于索引中.
举例:
若是惟一性索引创建在表的A列和B列上, 而且表中存在一条记录的A,B值为(123,null) , ORACLE将不接受下一条具备相同A,B值(123,null)的记录(插入). 然而若是
全部的索引列都为空,ORACLE将认为整个键值为空而空不等于空. 所以你能够插入1000
条具备相同键值的记录,固然它们都是空!
由于空值不存在于索引列中,因此WHERE子句中对索引列进行空值比较将使ORACLE停用该索引.
举例:
低效: (索引失效)
SELECT …
FROM DEPARTMENT
WHERE DEPT_CODE IS NOT NULL;
高效: (索引有效)
SELECT …
FROM DEPARTMENT
WHERE DEPT_CODE >=0;
ORACLE SQL性能优化系列 (十二)
39. 老是使用索引的第一个列
若是索引是创建在多个列上, 只有在它的第一个列(leading column)被where子句引用时,优化器才会选择使用该索引.
译者按:
这也是一条简单而重要的规则. 见如下实例.
SQL> create table multiindexusage (inda number , indb number , descr varchar2(10));
Table created.
SQL> create index multindex on multiindexusage(inda,indb);
Index created.
SQL> set autotrace traceonly
SQL> select * from multiindexusagewhere inda = 1;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (BY INDEX ROWID) OF'MULTIINDEXUSAGE'
2 1 INDEX (RANGE SCAN) OF 'MULTINDEX'(NON-UNIQUE)
SQL> select * from multiindexusagewhere indb = 1;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (FULL) OF'MULTIINDEXUSAGE'
很明显, 当仅引用索引的第二个列时,优化器使用了全表扫描而忽略了索引
40. ORACLE内部操做
当执行查询时,ORACLE采用了内部的操做. 下表显示了几种重要的内部操做.
ORACLE Clause
内部操做
ORDER BY
SORT ORDER BY
UNION
UNION-ALL
MINUS
MINUS
INTERSECT
INTERSECT
DISTINCT,MINUS,INTERSECT,UNION
SORT UNIQUE
MIN,MAX,COUNT
SORT AGGREGATE
GROUP BY
SORT GROUP BY
ROWNUM
COUNT or COUNT STOPKEY
Queries involving Joins
SORT JOIN,MERGE JOIN,NESTED LOOPS
CONNECT BY
CONNECT BY
41. 用UNION-ALL 替换UNION ( 若是有可能的话)
当SQL语句须要UNION两个查询结果集合时,这两个结果集合会以UNION-ALL的方式被合并, 而后在输出最终结果前进行排序.
若是用UNION ALL替代UNION, 这样排序就不是必要了. 效率就会所以获得提升.
举例:
低效:
SELECT ACCT_NUM, BALANCE_AMT
FROM DEBIT_TRANSACTIONS
WHERE TRAN_DATE = ’31-DEC-95’
UNION
SELECT ACCT_NUM, BALANCE_AMT
FROM DEBIT_TRANSACTIONS
WHERE TRAN_DATE = ’31-DEC-95’
高效:
SELECT ACCT_NUM, BALANCE_AMT
FROM DEBIT_TRANSACTIONS
WHERE TRAN_DATE = ’31-DEC-95’
UNION ALL
SELECT ACCT_NUM, BALANCE_AMT
FROM DEBIT_TRANSACTIONS
WHERE TRAN_DATE = ’31-DEC-95’
译者按:
须要注意的是,UNION ALL 将重复输出两个结果集合中相同记录. 所以各位仍是
要从业务需求分析使用UNION ALL的可行性.
UNION 将对结果集合排序,这个操做会使用到SORT_AREA_SIZE这块内存. 对于这
块内存的优化也是至关重要的. 下面的SQL能够用来查询排序的消耗量
Select substr(name,1,25) "SortArea Name",
substr(value,1,15) "Value"
from v$sysstat
where name like 'sort%'
42. 使用提示(Hints)
对于表的访问,能够使用两种Hints.
FULL 和 ROWID
FULL hint 告诉ORACLE使用全表扫描的方式访问指定表.
例如:
SELECT /*+ FULL(EMP) */ *
FROM EMP
WHERE EMPNO = 7893;
ROWID hint 告诉ORACLE使用TABLE ACCESS BY ROWID的操做访问表.
一般, 你须要采用TABLEACCESS BY ROWID的方式特别是当访问大表的时候, 使用这种方式, 你须要知道ROIWD的值或者使用索引.
若是一个大表没有被设定为缓存(CACHED)表而你但愿它的数据在查询结束是仍然停留
在SGA中,你就能够使用CACHE hint 来告诉优化器把数据保留在SGA中. 一般CACHE hint 和 FULL hint 一块儿使用.
例如:
SELECT /*+ FULL(WORKER) CACHE(WORKER)*/*
FROM WORK;
索引hint 告诉ORACLE使用基于索引的扫描方式. 你没必要说明具体的索引名称
例如:
SELECT /*+ INDEX(LODGING) */ LODGING
FROM LODGING
WHERE MANAGER = ‘BILL GATES’;
在不使用hint的状况下, 以上的查询应该也会使用索引,然而,若是该索引的重复值过多而你的优化器是CBO, 优化器就可能忽略索引. 在这种状况下, 你能够用INDEX hint强制ORACLE使用该索引.
ORACLE hints 还包括ALL_ROWS, FIRST_ROWS, RULE,USE_NL, USE_MERGE, USE_HASH 等等.
译者按:
使用hint , 表示咱们对ORACLE优化器缺省的执行路径不满意,须要手工修改.
这是一个颇有技巧性的工做. 我建议只针对特定的,少数的SQL进行hint的优化.
对ORACLE的优化器仍是要有信心(特别是CBO)
ORACLE SQL性能优化系列 (十三)
43. 用WHERE替代ORDER BY
ORDER BY 子句只在两种严格的条件下使用索引.
ORDER BY中全部的列必须包含在相同的索引中并保持在索引中的排列顺序.
ORDER BY中全部的列必须定义为非空.
WHERE子句使用的索引和ORDER BY子句中所使用的索引不能并列.
例如:
表DEPT包含如下列:
DEPT_CODE PK NOT NULL
DEPT_DESC NOT NULL
DEPT_TYPE NULL
非惟一性的索引(DEPT_TYPE)
低效: (索引不被使用)
SELECT DEPT_CODE
FROM DEPT
ORDER BY DEPT_TYPE
EXPLAIN PLAN:
SORT ORDER BY
TABLE ACCESS FULL
高效: (使用索引)
SELECT DEPT_CODE
FROM DEPT
WHERE DEPT_TYPE > 0
EXPLAIN PLAN:
TABLE ACCESS BY ROWID ON EMP
INDEX RANGE SCAN ON DEPT_IDX
译者按:
ORDER BY 也能使用索引! 这的确是个容易被忽视的知识点. 咱们来验证一下:
SQL> select * from emp order byempno;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (BY INDEX ROWID) OF'EMP'
2 1 INDEX (FULL SCAN) OF 'EMPNO'(UNIQUE)
44. 避免改变索引列的类型.
当比较不一样数据类型的数据时, ORACLE自动对列进行简单的类型转换.
假设 EMPNO是一个数值类型的索引列.
SELECT …
FROM EMP
WHERE EMPNO = ‘123’
实际上,通过ORACLE类型转换, 语句转化为:
SELECT …
FROM EMP
WHERE EMPNO = TO_NUMBER(‘123’)
幸运的是,类型转换没有发生在索引列上,索引的用途没有被改变.
如今,假设EMP_TYPE是一个字符类型的索引列.
SELECT …
FROM EMP
WHERE EMP_TYPE = 123
这个语句被ORACLE转换为:
SELECT …
FROM EMP
WHERE TO_NUMBER(EMP_TYPE)=123
由于内部发生的类型转换, 这个索引将不会被用到!
译者按:
为了不ORACLE对你的SQL进行隐式的类型转换, 最好把类型转换用显式表现出来. 注意当字符和数值比较时, ORACLE会优先转换数值类型到字符类型.
45. 须要小心的WHERE子句
某些SELECT 语句中的WHERE子句不使用索引. 这里有一些例子.
在下面的例子里, ‘!=’ 将不使用索引. 记住, 索引只能告诉你什么存在于表中, 而不能告诉你什么不存在于表中.
不使用索引:
SELECT ACCOUNT_NAME
FROM TRANSACTION
WHERE AMOUNT !=0;
使用索引:
SELECT ACCOUNT_NAME
FROM TRANSACTION
WHERE AMOUNT >0;
下面的例子中, ‘||’是字符链接函数. 就象其余函数那样, 停用了索引.
不使用索引:
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’;
下面的例子中, ‘+’是数学函数. 就象其余数学函数那样, 停用了索引.
不使用索引:
SELECT ACCOUNT_NAME, AMOUNT
FROM TRANSACTION
WHERE AMOUNT + 3000 >5000;
使用索引:
SELECT ACCOUNT_NAME, AMOUNT
FROM TRANSACTION
WHERE AMOUNT > 2000 ;
下面的例子中,相同的索引列不能互相比较,这将会启用全表扫描.
不使用索引:
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,’%’);
译者按:
若是必定要对使用函数的列启用索引, ORACLE新的功能: 基于函数的索引(Function-Based Index) 也许是一个较好的方案.
CREATE INDEX EMP_I ON EMP(UPPER(ename)); /*创建基于函数的索引*/
SELECT * FROM emp WHERE UPPER(ename) = ‘BLACKSNAIL’; /*将使用索引*/
ORACLE SQL性能优化系列 (十四) 完结篇
46. 链接多个扫描
若是你对一个列和一组有限的值进行比较, 优化器可能执行屡次扫描并对结果进行合并链接.
举例:
SELECT *
FROM LODGING
WHERE MANAGER IN (‘BILL GATES’,’KENMULLER’);
优化器可能将它转换成如下形式
SELECT *
FROM LODGING
WHERE MANAGER = ‘BILL GATES’
OR MANAGER = ’KEN MULLER’;
当选择执行路径时, 优化器可能对每一个条件采用LODGING$MANAGER上的索引范围扫描. 返回的ROWID用来访问LODGING表的记录 (经过TABLE ACCESS BY ROWID 的方式). 最后两组记录以链接(CONCATENATION)的形式被组合成一个单一的集合.
Explain Plan :
SELECT STATEMENT Optimizer=CHOOSE
CONCATENATION
TABLE ACCESS (BY INDEX ROWID) OFLODGING
INDEX (RANGE SCAN ) OF LODGING$MANAGER(NON-UNIQUE)
TABLE ACCESS (BY INDEX ROWID) OFLODGING
INDEX (RANGE SCAN ) OF LODGING$MANAGER(NON-UNIQUE)
译者按:
本节和第37节彷佛有矛盾之处.
47. CBO下使用更具选择性的索引
基于成本的优化器(CBO, Cost-Based Optimizer)对索引的选择性进行判断来决定索引的使用是否能提升效率.
若是索引有很高的选择性, 那就是说对于每一个不重复的索引键值,只对应数量不多的记录.
好比, 表中共有100条记录而其中有80个不重复的索引键值. 这个索引的选择性就是80/100 = 0.8 . 选择性越高,经过索引键值检索出的记录就越少.
若是索引的选择性很低, 检索数据就须要大量的索引范围查询操做和ROWID 访问表的
操做. 也许会比全表扫描的效率更低.
译者按:
下列经验请参阅:
a. 若是检索数据量超过30%的表中记录数.使用索引将没有显著的效率提升.
b. 在特定状况下, 使用索引也许会比全表扫描慢, 但这是同一个数量级上的
区别. 而一般状况下,使用索引比全表扫描要块几倍乃至几千倍!
48. 避免使用耗费资源的操做
带有DISTINCT,UNION,MINUS,INTERSECT,ORDER BY的SQL语句会启动SQL引擎
执行耗费资源的排序(SORT)功能.DISTINCT须要一次排序操做, 而其余的至少须要执行两次排序.
例如,一个UNION查询,其中每一个查询都带有GROUP BY子句,GROUP BY会触发嵌入排序(NESTED SORT) ; 这样, 每一个查询须要执行一次排序, 而后在执行UNION时, 又一个惟一排序(SORTUNIQUE)操做被执行并且它只能在前面的嵌入排序结束后才能开始执行. 嵌入的排序的深度会大大影响查询的效率.
一般, 带有UNION,MINUS , INTERSECT的SQL语句均可以用其余方式重写.
译者按:
若是你的数据库的SORT_AREA_SIZE调配得好, 使用UNION , MINUS, INTERSECT也是能够考虑的, 毕竟它们的可读性很强
49. 优化GROUP BY
提升GROUP BY 语句的效率, 能够经过将不须要的记录在GROUP BY 以前过滤掉.下面两个查询返回相同结果但第二个明显就快了许多.
低效:
SELECT JOB , AVG(SAL)
FROM EMP
GROUP JOB
HAVING JOB = ‘PRESIDENT’
OR JOB = ‘MANAGER’
高效:
SELECT JOB , AVG(SAL)
FROM EMP
WHERE JOB = ‘PRESIDENT’
OR JOB = ‘MANAGER’
GROUP JOB
译者按:
本节和14节相同. 可略过.
50. 使用日期
当使用日期是,须要注意若是有超过5位小数加到日期上, 这个日期会进到下一天!
例如:
1.
SELECT TO_DATE(‘01-JAN-93’+.99999)
FROM DUAL;
Returns:
’01-JAN-93 23:59:59’
2.
SELECT TO_DATE(‘01-JAN-93’+.999999)
FROM DUAL;
Returns:
’02-JAN-93 00:00:00’
译者按:
虽然本节和SQL性能优化没有关系, 可是做者的功力可见一斑
51. 使用显式的游标(CURSORs)
使用隐式的游标,将会执行两次操做. 第一次检索记录, 第二次检查TOO MANY ROWS 这个exception . 而显式游标不执行第二次操做.
52. 优化EXPORT和IMPORT
使用较大的BUFFER(好比10MB, 10,240,000)能够提升EXPORT和IMPORT的速度.
ORACLE将尽量地获取你所指定的内存大小,即便在内存不知足,也不会报错.这个值至少要和表中最大的列至关,不然列值会被截断.
译者按:
能够确定的是, 增长BUFFER会大大提升EXPORT , IMPORT的效率. (曾经碰到过一个CASE, 增长BUFFER后,IMPORT/EXPORT快了10倍!)
做者可能犯了一个错误: “这个值至少要和表中最大的列至关,不然列值会被截断. “
其中最大的列也许是指最大的记录大小.
关于EXPORT/IMPORT的优化,CSDN论坛中有一些总结性的贴子,好比关于BUFFER参数,COMMIT参数等等, 详情请查.
53. 分离表和索引
老是将你的表和索引创建在不一样的表空间内(TABLESPACES). 决不要将不属于ORACLE内部系统的对象存放到SYSTEM表空间里. 同时,确保数据表空间和索引表空间置于不一样的硬盘上.
译者按:
“同时,确保数据表空间和索引表空间置与不一样的硬盘上.”可能改成以下更为准确 “同时,确保数据表空间和索引表空间置与不一样的硬盘控制卡控制的硬盘上.”
5、 SQL编程
数据库表、列
列出数据库里全部的表名
select table_name from user_tables; --当前用户的表
select table_name from all_tables; --全部用户的表
select table_name from dba_tables; --包括系统表
列出表里的全部的列
desc table_name
表备份
综合评价:★★
下面的例子会制做 "persons" 表的备份复件:
select * into persons_backup from persons
in 子句可用于向另外一个数据库中拷贝表:
select * into persons in 'backup.mdb' from persons
事务
事务:做为一个逻辑单元执行的一系列操做,一个逻辑工做单元必须有四个属性,称为 acid(原子性、一致性、隔离性和持久性)属性。
原子性:事务必须是原子工做单元;对于其数据修改,要么全都执行,要么全都不执行。
一致性:事务在完成时,必须使全部的数据都保持一致状态。在相关数据库中,全部规则都必须应用于事务的修改,以保持全部数据的完整性。事务结束时,全部的内部数据结构(如 b 树索引或双向链表)都必须是正确的。
隔离性:由并发事务所做的修改必须与任何其它并发事务所做的修改隔离。事务查看数据时所处的状态,要么是另外一并发事务修改它以前的状态,要么是另外一事务修改它以后的状态,事务不会查看中间状态的数据。这称为可串行性,由于它可以从新装载起始数据,而且重播一系列事务,以使数据结束时的状态与原始事务执行的状态相同。
持久性:事务完成以后,它对于系统的影响是永久性的。该修改即便出现系统故障也将一直保持。
锁:共享锁、互斥锁
共享锁:若是事务t对数据a加上共享锁后,则其余事务只能对a再加共享锁,不能加排他锁,直到已释放全部共享锁。获准共享锁的事务只能读数据,不能修改数据。
排他锁:若是事务t对数据a加上排他锁后,则其余事务不能再对a加任任何类型的锁,直到在事务的末尾将资源上的锁释放为止。获准排他锁的事务既能读数据,又能修改数据。
两段锁协议:阶段1:加锁阶段阶段2:解锁阶段
触发器
触发器: 当知足触发器条件,则系统自动执行触发器的触发体。
触发时间:有before,after.触发事件:有insert,update,delete三种。触发类型:有行触发、语句触发
触发器的做用:触发器是一中特殊的存储过程,主要是经过事件来触发而被执行的。它能够强化约束,来维护数据的完整性和一致性,能够跟踪数据库内的操做从而不容许未经许可的更新和变化。能够联级运算。如,某表上的触发器上包含对另外一个表的数据操做,而该操做又会致使该表触发器被触发。
事前触发器运行于触发事件发生以前,而过后触发器运行于触发事件发生以后。一般事前触发器能够获取事件以前和新的字段值。
语句级触发器能够在语句执行前或后执行,而行级触发在触发器所影响的每一行触发一次。
视图、游标
综合评价:★★★
视图是一种虚拟的表,具备和物理表相同的功能。能够对视图进行增,改,查,操做,视图一般是有一个表或者多个表的行或列的子集。对视图的修改不影响基本表。它使得咱们获取数据更容易,相比多表查询。
游标:一个游标(cursor)能够被看做指向结果集(a set of rows)中一行的指针(pointer)。游标每一个时间点只能指向一行,可是能够根据须要指向结果集中其余的行。
例如:SELECT * FROM employees WHERE sex='M'会返回全部性别为男的雇员,在初始的时候,游标被放置在结果集中第一行的前面。使游标指向第一行,要执行FETCH。当游标指向结果集中一行的时候,能够对这行数据进行加工处理,要想获得下一行数据,要继续执行FETCH。FETCH操做能够重复执行,直到完成结果集中的全部行
在存储过程当中使用游标:
声明游标、打开游标、根据须要一次一行,讲游标指向的数据取到本地变量(local variables)中、结束时关闭游标。
显式游标:当查询返回结果超过一行时,就须要一个显式游标,此时用户不能使用select into语句。PL/SQL管理隐式游标,当查询开始时隐式游标打开,查询结束时隐式游标自动关闭。显式游标在PL/SQL块的声明部分声明,在执行部分 或异常处理部分打开,取出数据,关闭。
使用游标:咱们所说的游标一般是指 显式游标
CURSOR cursor_name IS select_statement;
在PL/SQL中游标名是一个未声明变量,不能给游标名赋值或用于表达式中。例:
DELCARE CURSOR C_EMP IS SELECT empno,ename,salary FROM emp WHERE salary>2000 ORDER BY ename; ........ BEGIN |
打开关闭游标:使用游标中的值以前应该首先打开游标初始化查询处理。
OPEN cursor_name; CLOSE cursor_name |
从游标提取数据:从游标获得一行数据使用FETCH命令。每一次提取数据后,游标都指向结果集的下一行。语法以下:FETCHcursor_name INTO variable [, variable...]
对于SELECT定义的游标的每一列,FETCH变量列表都应该有一个变量与之相对应,变量的类型也要相同。返回结果集不止一条就要使用循环。例:
SET SERVERIUTPUT ON v_ename EMP.ENAME%TYPE; v_salary EMP.SALARY%TYPE; CURSOR c_emp IS SELECT ename,salary FROM emp; BEGIN OPEN c_emp; LOOP FETCH c_emp INTO v_ename,v_salary; EXIT WHEN c_emp%NOTFOUND; DBMS_OUTPUT.PUT_LINE(/'Salary of Employee/'|| v_ename ||/'is/'|| v_salary);
END; |
记录变量:定义一个记录变量使用TYPE命令和%ROWTYPE。
记录变量用于从游标中提取数据行,当游标选择不少列的时候,那么使用记录比为每列声明一个变量要方便得多。
关 键 词:当在表上使用%ROWTYPE并将从游标中取出的值放入记录中时,若是要选择表中全部列,那么在SELECT子句中使用*比将全部列名列出来要得多。例:
SET SERVERIUTPUT ON DECLARE R_emp EMP%ROWTYPE; CURSOR c_emp IS SELECT * FROM emp; BEGIN OPEN c_emp; LOOP FETCH c_emp INTO r_emp; EXIT WHEN c_emp%NOTFOUND; DBMS_OUT.PUT.PUT_LINE(/'Salary of Employee/'||r_emp.ename||/'is/'|| r_emp.salary); END LOOP; CLOSE c_emp; END; |
%ROWTYPE也能够用游标名来定义,这样的话就必需要首先声明游标:
SET SERVERIUTPUT ON DECLARE CURSOR c_emp IS SELECT ename,salary FROM emp; R_emp c_emp%ROWTYPE; BEGIN OPEN c_emp; LOOP FETCH c_emp INTO r_emp; EXIT WHEN c_emp%NOTFOUND; DBMS_OUT.PUT.PUT_LINE(/'Salary of Employee/' ||r_emp.ename||/'is/'|| r_emp.salary); END LOOP; CLOSE c_emp; END; |
带参数的游标:与存储过程和函数类似,能够将参数传递给游标并在查询中使用。语法以下:
CURSOR cursor_name[(parameter[,parameter],...)] IS select_statement; |
定义参数的:
Parameter_name [IN] data_type[{:=|DEFAULT} value] |
游标只能接受传递的值,不能返回值。参数只定义数据类型,没有大小。另外能够给参数设定一个缺省值,当没有参数值传递给游标时,就使用缺省值。
打开游标时给参数赋值:
OPEN cursor_name [value [, value]....]; |
参数值能够是文字或变量。例:
DECALRE CURSOR c_dept IS SELECT * FROM dept ORDER BY deptno; CURSOR c_emp (p_dept VARACHAR2) IS SELECT ename,salary FROM emp WHERE deptno=p_dept ORDER BY ename r_dept DEPT%ROWTYPE; v_ename EMP.ENAME%TYPE; v_salary EMP.SALARY%TYPE; v_tot_salary EMP.SALARY%TYPE; BEGIN OPEN c_dept; LOOP FETCH c_dept INTO r_dept; EXIT WHEN c_dept%NOTFOUND; DBMS_OUTPUT.PUT_LINE (/'Department:/'|| r_dept.deptno||/'-/'||r_dept.dname); v_tot_salary:=0; OPEN c_emp(r_dept.deptno); LOOP FETCH c_emp INTO v_ename,v_salary; EXIT WHEN c_emp%NOTFOUND; DBMS_OUTPUT.PUT_LINE (/'Name:/'|| v_ename||/' salary:/'||v_salary); v_tot_salary:=v_tot_salary+v_salary; END LOOP; CLOSE c_emp; DBMS_OUTPUT.PUT_LINE (/'Toltal Salary for dept:/'|| v_tot_salary); END LOOP; CLOSE c_dept; END; |
游标FOR循环
在大多数时候咱们在设计程序的时候都遵循下面的步骤:
一、打开游标。
二、开始循环。
三、从游标中取值。
四、那一行被返回。
五、处理。
六、关闭循环。
七、关闭游标。
能够简单的把这一类代码称为游标用于循环。但还有一种循环与这种类型不相同,这就是FOR循环,用于FOR循环的游标按照正常的声明方式声明,它的优势在于不须要显式的打开、关闭、取数据,测试数据的存在、定义存放数据的变量等等。游标FOR循环的语法以下:
FOR record_name IN (corsor_name[(parameter[,parameter]...)] | (query_difinition) LOOP statements END LOOP; |
用for循环重写上面的例子:
DECALRE CURSOR c_dept IS SELECT deptno,dname FROM dept ORDER BY deptno; CURSOR c_emp (p_dept VARACHAR2) IS SELECT ename,salary FROM emp WHERE deptno=p_dept ORDER BY ename v_tot_salary EMP.SALARY%TYPE; BEGIN FOR r_dept IN c_dept LOOP DBMS_OUTPUT.PUT_LINE (/'Department:/'|| r_dept.deptno||/'-/'||r_dept.dname); v_tot_salary:=0; FOR r_emp IN c_emp(r_dept.deptno) LOOP DBMS_OUTPUT.PUT_LINE (/'Name:/' || v_ename || /'salary:/' || v_salary); v_tot_salary:=v_tot_salary+v_salary; END LOOP; DBMS_OUTPUT.PUT_LINE (/'Toltal Salary for dept:/'|| v_tot_salary); END LOOP; END; |
在游标FOR循环中使用查询
在游标FOR循环中能够定义查询,因为没有显式声明因此游标没有名字,记录名经过游标查询来定义。
DECALRE v_tot_salary EMP.SALARY%TYPE; BEGIN FOR r_dept IN (SELECT deptno,dname FROM dept ORDER BY deptno) LOOP DBMS_OUTPUT.PUT_LINE(/'Department:/'|| r_dept.deptno||/'-/'||r_dept.dname); v_tot_salary:=0; FOR r_emp IN (SELECT ename,salary FROM emp WHERE deptno=p_dept ORDER BY ename) LOOP DBMS_OUTPUT.PUT_LINE(/'Name:/'|| v_ename||/' salary:/'||v_salary); v_tot_salary:=v_tot_salary+v_salary; END LOOP; DBMS_OUTPUT.PUT_LINE(/'Toltal Salary for dept:/'|| v_tot_salary); END LOOP; END; |
游标中的子查询
CURSOR C1 IS SELECT * FROM emp WHERE deptno NOT IN (SELECT deptno FROM dept WHERE dname!=/'ACCOUNTING/'); |
游标中的更新和删除(没学)
在PL/SQL中依然能够使用UPDATE和DELETE语句更新或删除数据行。显式游标只有在须要得到多行数据的状况下使用。PL/SQL提供了仅仅使用游标就能够执行删除或更新记录的方法。
UPDATE或DELETE语句中的WHERECURRENT OF子串专门处理要执行UPDATE或DELETE操做的表中取出的最近的数据。要使用这个方法,在声明游标时必须使用FOR UPDATE子串,当对话使用FOR UPDATE子串打开一个游标时,全部返回集中的数据行都将处于行级(ROW-LEVEL)独占式锁定,其余对象只能查询这些数据行,不能进行 UPDATE、DELETE或SELECT...FOR UPDATE操做。
语法:
FOR UPDATE [OF [schema.]table.column[,[schema.]table.column].. [nowait] |
在多表查询中,使用OF子句来锁定特定的表,若是忽略了OF子句,那么全部表中选择的数据行都将被锁定。若是这些数据行已经被其余会话锁定,那么正常状况下ORACLE将等待,直到数据行解锁。
在UPDATE和DELETE中使用WHERECURRENT OF子串的语法以下:
WHERE{CURRENT OF cursor_name|search_condition} DELCARE CURSOR c1 IS SELECT empno,salary FROM emp WHERE comm IS NULL FOR UPDATE OF comm; v_comm NUMBER(10,2); BEGIN FOR r1 IN c1 LOOP IF r1.salary<500 THEN v_comm:=r1.salary*0.25; ELSEIF r1.salary<1000 THEN v_comm:=r1.salary*0.20; ELSEIF r1.salary<3000 THEN v_comm:=r1.salary*0.15; ELSE v_comm:=r1.salary*0.12; END IF; UPDATE emp; SET comm=v_comm WHERE CURRENT OF c1l; END LOOP; END |
存储过程
综合评价:★★★★
存储过程基础知识
存储过程:一组为了完成特定功能的SQL语句集,经编译后存储在数据库中。
存储过程的建立须要CREATE PROCEDURE 系统权限,若是须要被其余用户Schema使用须要CREATE ANY PROCEDURE 权限。存储过程的执行须要 EXECUTE权限或者 EXECUTE ANY PROCEDURE 权限。
单独赋予权限:grant execute on MY_PROCEDURE to Jelly;
调用存储过程: executeMY_PROCEDURE( 'ONE PARAMETER');
存储过程(PROCEDURE)和函数(FUNCTION)的区别:
A: 函数有限制只能返回一个标量,而存储过程能够返回多个;
B: 函数能够嵌入到SQL语句中执行. 而存储过程不行。存储过程执行必须调用EXECUTE
包(PACKAGE)是function,procedure,variables 和SQL 语句的组合。package容许多个procedure使用同一个变量和游标。
存储过程的特色:
1.存储过程运行的速度比较快。
2. 可保证数据的安全性和完整性。
3.能够下降网络的通讯量。
4:存储过程能够接受参数、输出参数、返回单个或多个结果集以及返回值。
5:存储过程能够包含程序流、逻辑以及对数据库的查询。
存储过程建立语法
CREATE [ OR REPLACE ] PROCEDURE [ schema.]procedure [(argument [IN | OUT | IN OUT ] [NO COPY] datatype [, argument [IN | OUT | IN OUT ] [NO COPY] datatype]... )] [ authid { current_user | definer }] { is | as } { pl/sql_subprogram_body | language { Java name 'String' | c [ name, name] library lib_name }] |
Sql 代码:
CREATE PROCEDURE sam.credit (acc_no IN NUMBER, amount IN NUMBER) AS BEGIN UPDATE accounts SET balance = balance + amount WHERE account_id = acc_no; END; |
IN, OUT, IN OUT用来修饰参数。
IN 表示这个变量必须被调用者赋值而后传入到PROCEDURE进行处理。
OUT 表示PRCEDURE 经过这个变量将值传回给调用者。
IN OUT 则是这两种的组合。
authid表明两种权限:
定义者权限(difiner right 默认),执行者权限(invoker right)。
定义者权限说明这个procedure中涉及的表、视图等对象所须要的权限只要定义者拥有权限的话就能够访问。
执行者权限则须要调用这个 procedure的用户拥有相关表和对象的权限。
Oracle存储过程的语法
1. 基本结构
CREATE OR REPLACE PROCEDURE 存储过程名字 END 存储过程名字 |
2. SELECT INTO STATEMENT
将select查询的结果存入到变量中,能够同时将多个列存储多个变量中,必须有一条记录,不然抛出异常(若是没有记录抛出NO_DATA_FOUND)
例子:
BEGIN WHEN OTHERS THEN xxxx; |
3. IF 判断
IF V_TEST=1 THEN |
4. while 循环
WHILE V_TEST=1 LOOP |
5. 变量赋值
V_TEST := 123; |
6. 用for in 使用cursor
... |
7. 带参数的cursor
CURSOR C_USER(C_ID NUMBER) IS SELECT NAME FROM USER WHERE TYPEID=C_ID; |
8. 用pl/sql developer debug
链接数据库后创建一个Test WINDOW
在窗口输入调用SP的代码,F9开始debug,CTRL+N单步调试
9. Pl/Sql中执行存储过程
在sql*plus中:
declare |
在SQL/PLUS中调用存储过程,显示结果:
SQL>set serveoutput on --打开输出 SQL>var info1 number; --输出1 SQL>var info2 number; --输出2 SQL>declare var1 varchar2(20); --输入1 var2 varchar2(20); --输入2 var3 varchar2(20); --输入2 BEGIN pro(var1,var2,var3,:info1,:info2); END; / SQL>print info1; SQL>print info2; |
注:在EXECUTE IMMEDIATE STR语句是SQLPLUS中动态执行语句,它在执行中会自动提交,相似于DP中FORMS_DDL语句,在此语句中STR是不能换行的,只能经过链接字符"||",或者在换行时加上"-"链接字符。
绑定变量
绑定变量是指在SQL语句中使用变量,改变变量的值来改变SQL语句的执行结果。
优势:使用绑定变量,能够减小SQL语句的解析,能减小数据库引擎消耗在SQL语句解析上的资源。提升了编程效率和可靠性。减小访问数据库的次数, 就能实际上减小oracle的工做量。
缺点:常常须要使用动态SQL的写法,因为参数的不一样,可能SQL的执行效率不一样;绑定变量是相对文本变量来说的,所谓文本变量是指在SQL直接书写查询条件,这样的SQL在不一样条件下须要反复解析,绑定变量是指使用变量来代替直接书写条件,查询bind value在运行时传递,而后绑定执行。优势是减小硬解析,下降cpu的争用,节省shared_pool缺点是不能使用histogram,SQL优化比较困难
索引
综合评价:★★★
select *from user_indexes 查询现有的索引
select *from user_ind_columns 可获知索引创建在那些字段上
一、 什么是索引?、
一种用于提高查询效率的数据库对象;经过快速定位数据的方法,减小磁盘I/O操做;索引信息与表独立存放;Oracle数据库自动使用和维护索引。
二、 索引分类?
惟一索引和非惟一索引
三、 建立索引的两种方式?
自动建立,在定义主键或惟一键约束是系统会自动在相应的字段上建立惟一性索引。
手动建立,用户能够在其余列上建立非惟一索引,加速查询。
四、 索引优缺点
索引的优势
1.大大加快数据的检索速度;
2.建立惟一性索引,保证数据库表中每一行数据的惟一性;
3.加速表和表之间的链接;
4.在使用分组和排序子句进行数据检索时,能够显著减小查询中分组和排序的时间。
有索引且查询条件能使用索引时,数据库会先选取索引,根据索引内容和查询条件,查询出rowid,再根据rowid取出须要的数据。因为索引内容一般比全表内容要少不少,所以经过先读索引,能减小i/o,提升查询性能。
索引的缺点
1.索引须要占物理空间。
2.当对表中的数据进行增长、删除和修改的时候,索引也要动态的维护,下降了数据的维护速度。
五、 建立索引的原则
建立索引:建立索引通常有如下两个目的:维护被索引列的惟一性和提供快速访问表中数据
的策略。
--在select 操做占大部分的表上建立索引;
--在where 子句中出现最频繁的列上建立索引;
--在选择性高的列上建立索引(补充索引选择性,最高是1,eg:primary key)
--复合索引的主列应该是最有选择性的和where 限定条件最经常使用的列,并以此类推第二列……。
--小于5M 的表,最好不要使用索引来查询,表越小,越适合用全表扫描。
六、 使用索引的原则
--查询结果是全部数据行的5%如下时,使用index 查询效果最好;
--where 条件中常常用到表的多列时,使用复合索引效果会好于几个单列索引。由于当sql
语句所查询的列,所有都出如今复合索引中时,此时因为Oracle 只须要查询索引块便可获
得全部数据,固然比使用多个单列索引要快得多;
--索引利于select,但对常常insert,delte尤为update 的表,会下降效率。
eg:试比较下面两条SQL 语句(emp 表的deptno 列上建有ununique index):
语句A:SELECT dname, deptno FROM dept WHEREdeptno NOT IN
(SELECTdeptno FROM emp);
语句B:SELECT dname, deptno FROM dept WHERE NOTEXISTS
(SELECTdeptno FROM emp WHERE dept.deptno = emp.deptno);
注意:这两条查询语句实现的结果是相同的,可是执行语句A 的时候,ORACLE 会对整个emp 表进行扫描,没有使用创建在emp 表上的deptno 索引,执行语句B 的时候,因为在子查询中使用了联合查询,ORACLE 只是对emp 表进行的部分数据扫描,并利用了deptno 列的索引,
因此语句B 的效率要比语句A 的效率高。
----where子句中的这个字段,必须是复合索引的第一个字段;
eg:一个索引是按f1, f2, f3 的次序创建的,若where 子句是f2 = : var2, 则由于f2 不是索引的第1 个字段,没法使用该索引。
---- where 子句中的这个字段,不该该参与任何形式的计算:任何对列的操做都将致使表扫描,它包括数据库函数、计算表达式等等,查询时要尽量将操做移至等号右边。
---- 应尽可能熟悉各类操做符对Oracle 是否使用索引的影响:如下这些操做会显式( explicitly )地阻止Oracle 使用索引: is null ; is not null; not in; !=; like ;
numeric_col+0;date_col+0;char_col||' '; to_char; to_number,to_date 等。
Eg:select jobid from mytabs where isReq='0' and to_date (updatedate)>= to_Date ( '2001-7-18','YYYY-MM-DD');--updatedate 列的索引也不会生效。
七、 建立索引
createindex abc on student(sid,sname);
createindex abc1 on student(sname,sid);
这两种索引方式是不同的,索引abc 对Select * from student where sid=1; 这样的查询语句更有效索引abc1 对Select * from student where sname=’louis’; 这样的查询语句更有效所以创建索引的时候,字段的组合顺序是很是重要的。通常状况下,须要常常访问的字段放在组合字段的前面
八、 索引的存储
索引和表都是独立存在的。在为索引指定表空间的时候,不要将被索引的表和索引指向同
一个表空间,这样能够避免产生IO 冲突。使Oracle 可以并行访问存放在不一样硬盘中的索引数据和表数据,更好的提升查询速度。
九、 删除索引
dropindex PK_DEPT1;
十、 索引类型
索引有b-tree、bit、cluster等类型。oracle使用了一个复杂的自平衡b-tree结构;
B 树索引(B-Tree Index)
建立索引的默认类型,结构是一颗树,采用的是平衡B 树算法:
右子树节点的键值大于等于父节点的键值;左子树节点的键值小于等于父节点的键值
好比有数据:100,101,102,103,104,105,106
位图索引(BitMap Index)
若是表中的某些字段取值范围比较小,好比职员性别、分数列ABC 级等。只有两个值。
这样的字段若是建B 树索引没有意义,不能提升检索速度。这时咱们推荐用位图索引
CreateBitMap Index student on(sex);
十一、 管理索引
1)先插入数据后建立索引
2)设置合理的索引列顺序
3)限制每一个表索引的数量
4)删除没必要要的索引
5)为每一个索引指定表空间
6)常常作insert,delete 尤为是update的表最好按期exp/imp 表数据,整理数据,下降碎片(缺点:要停应用,以保持数据一致性,不实用);有索引的最好按期rebuild 索引(rebuild期间只容许表的select 操做,可在数据库较空闲时间提交),以下降索引碎片,提升效率
6、 oracle数据库
exp、imp备份数据库
综合评价:★★★
1)命令行备份,如:
a)将数据库test彻底导出,用户名system 密码manager 导出到d:\daochu.dmp中,
exp system/manager@test file=d:\daochu.dmp full=y
b)将数据库中system用户与sys用户的表导出 ,
expsystem/manager@test file=d:\daochu.dmp owner=(system,sys)
c)将数据库中的表inner_notify、notify_staff_relat导出
expaichannel/aichannel@testdb2 file= d:\data\newsmgnt.dmp
tables=(inner_notify,notify_staff_relat)
详细内容请查阅oracle相关资料
2)dmp文件导入
将d:\daochu.dmp中的数据导入 test数据库中,
imp system/manager@test file=d:\daochu.dmp,若是已经存在须要导入的表,则使用以下命令imp system/manager@test file=d:\daochu.dmp ignore=y
将d:\daochu.dmp中的表table1 导入
imp system/manager@test file=d:\daochu.dmp tables=(table1)
不借助第三方工具,怎样查看SQL的执行计划
i) 使用explain plan,查询plan_table;
explainplan
setstatement_id='query1'
for
select *
from a
whereaa=1;
selectoperation, options, object_name, object_type, id, parent_id
fromplan_table
wherestatement_id = 'query1'
order byid;
ii)SQLplus中的set trace 便可看到execution plan statistics
setautotrace on;
如何使用cbo,cbo与rule的区别
if 初始化参数 optimizer_mode = choose then --(8i default)
if 作过表分析
then 优化器 optimizer=cbo(cost); /*高效*/
else
优化器 optimizer=rbo(rule); /*高效*/
end if;
end if;
区别:
rule根据规则选择最佳执行路径来运行查询。
cbo根据表统计找到最低成本的访问数据的方法肯定执行计划。
使用cbo须要注意:
i) 须要常常对表进行analyze命令进行分析统计;
ii) 须要稳定执行计划;
iii)须要使用提示(hint);
使用rule须要注意:
i) 选择最有效率的表名顺序
ii) 优化SQL的写法;
在optimizer_mode=choose时,若是表有统计信息(分区表外),优化器将选择cbo,不然选rbo。
rbo遵循简单的分级方法学,使用15种级别要点,当接收到查询,优化器将评估使用到的要点数目,而后选择最佳级别(最
少的数量)的执行路径来运行查询。
cbo尝试找到最低成本的访问数据的方法,为了最大的吞吐量或最快的初始响应时间,计算使用不一样的执行计划的成本,并
选择成本最低的一个,关于表的数据内容的统计被用于肯定执行计划。
如何定位重要(消耗资源多)的SQL
使用cpu多的用户session
selecta.sid, spid, status, substr (a.program, 1, 40) prog, a.terminal,a.SQL_text,osuser, value / 60 /
100value
fromv$session a, v$process b, v$sesstat c
wherec.statistic# = 12 and c.sid = a.sid and a.paddr = b.addr
order byvalue desc;
select SQL_textfrom v$SQL
wheredisk_reads > 1000 or (executions > 0 and buffer_gets/executions> 30000);
如何跟踪某个session的SQL
利用trace 跟踪
altersession set SQLtrace on;
column SQLformat a200;
selectmachine, SQL_text SQL
from v$SQLtexta, v$session b
whereaddress = SQL_address
andmachine = '&a'
order byhash_value, piece;
execdbms_system.set_SQL_trace_in_session(sid,serial#,&SQL_trace);
selectsid,serial# from v$session where sid = (select sid from v$mystat where rownum =1);
execdbms_system.set_ev(&sid,&serial#,&event_10046,&level_12,'');
如何稳定(固定)执行计划
能够在SQL语句中指定执行计划。使用hints;
query_rewrite_enabled= true
star_transformation_enabled= true
optimizer_features_enable= 9.2.0
建立并使用stored outline
pctused and pctfree 表示什么含义有什么做用
pctused与pctfree控制数据块是否出如今freelist中, pctfree控制数据块中保留用于update的空间,当数据块中的
freespace小于pctfree设置的空间时,该数据块从freelist中去掉,当块因为dml操做free space大于pct_used设置的空
间时,该数据库块将被添加在freelist链表中。
简单描述tablespace / segment / extent / block之间的关系
tablespace:一个数据库划分为一个或多个逻辑单位,该逻辑单位成为表空间;每个表空间可能包含一个或多个
segment;
segments:segment指在tablespace中为特定逻辑存储结构分配的空间。每个段是由一个或多个extent组成。包括数据
段、索引段、回滚段和临时段。
extents:一个 extent 由一系列连续的 oracle blocks组成.oracle为经过extent 来给segment分配空间。
datablocks:oracle 数据库最小的i/o存储单位,一个data block对应一个或多个分配给data file的操做系统块。
table建立时,默认建立了一个data segment,每一个data segment含有min extents指定的extents数,每一个extent据据表空
间的存储参数分配必定数量的blocks
描述tablespace和datafile之间的关系
一个表空间可包含一个或多个数据文件。表空间利用增长或扩展数据文件扩大表空间,表空间的大小为组成该表空间的
数据文件大小的和。一个datafile只能属于一个表空间;
一个tablespace能够有一个或多个datafile,每一个datafile只能在一个tablespace内, table中的数据,经过hash算法分布
在tablespace中的各个datafile中,tablespace是逻辑上的概念,datafile则在物理上储存了数据库的种种对象。
本地管理表空间和字典管理表空间的特色,assm有什么特色
本地管理表空间:(9i默认)空闲块列表存储在表空间的数据文件头。
特色:减小数据字典表的竞争,当分配和收缩空间时会产生回滚,不须要合并。
字典管理表空间:(8i默认)空闲块列表存储在数据库中的字典表里.
特色:片由数据字典管理,可能形成字典表的争用。存储在表空间的每个段都会有不一样的存储字句,须要合并相邻的
块;
本地管理表空间(locally managed tablespace简称lmt)
8i之后出现的一种新的表空间的管理模式,经过位图来管理表空间的空间使用。字典管理表空间(dictionary-managed
tablespace简称dmt)
8i之前包括之后都还能够使用的一种表空间管理模式,经过数据字典管理表空间的空间使用。动段空间管理(assm),
它首次出如今oracle920里有了assm,连接列表freelist被位图所取代,它是一个二进制的数组,
可以迅速有效地管理存储扩展和剩余区块(free block),所以可以改善分段存储本质,assm表空间上建立的段还有另
外一个称呼叫bitmap managed segments(bmb 段)。
回滚段的做用是什么
回滚段用于保存数据修改前的映象,这些信息用于生成读一致性数据库信息、在数据库恢复和rollback时使用。一个事
务只能使用一个回滚段。
事务回滚:当事务修改表中数据的时候,该数据修改前的值(即前影像)会存放在回滚段中,当用户回滚事务(
rollback)时,oracle将会利用回滚段中的数据前影像来将修改的数据恢复到原来的值。
事务恢复:当事务正在处理的时候,例程失败,回滚段的信息保存在undo表空间中,oracle将在下次打开数据库时利用
回滚来恢复未提交的数据。
读一致性:当一个会话正在修改数据时,其余的会话将看不到该会话未提交的修改。当一个语句正在执行时,该语句将
看不到从该语句开始执行后的未提交的修改(语句级读一致性)
当oracle执行select语句时,oracle依照当前的系统改变号(system change number-scn) 来保证任何前于当前scn的
未提交的改变不被该语句处理。能够想象:当一个长时间的查询正在执行时,若其余会话改变了该查询要查询的某个数
据块,oracle将利用回滚段的数据前影像来构造一个读一致性视图
日志的做用是什么
日志文件(log file)记录全部对数据库数据的修改,主要是保护数据库以防止故障,以及恢复数据时使用。其特色如
下:
a)每个数据库至少包含两个日志文件组。每一个日志文件组至少包含两个日志文件成员。
b)日志文件组以循环方式进行写操做。
c)每个日志文件成员对应一个物理文件。
记录数据库事务,最大限度地保证数据的一致性与安全性
重作日志文件:含对数据库所作的更改记录,这样万一出现故障能够启用数据恢复,一个数据库至少须要两个重作日志文
件
归档日志文件:是重作日志文件的脱机副本,这些副本可能对于从介质失败中进行恢复很必要。
sga主要有那些部分,主要做用是什么
系统全局区(sga):是oracle为实例分配的一组共享缓冲存储区,用于存放数据库数据和控制信息,以实现对数据库数
据的管理和操做。
sga主要包括:
a)共享池(shared pool) :用来存储最近执行的SQL语句和最近使用的数据字典的数据。
b)数据缓冲区 (database buffer cache):用来存储最近从数据文件中读写过的数据。
c)重做日志缓冲区(redo log buffer):用来记录服务或后台进程对数据库的操做。
另外在sga中还有两个可选的内存结构:
d)javapool: 用来存储Java代码。
e)largepool: 用来存储不与SQL直接相关的大型内存结构。备份、恢复使用。
ga:db_cache/shared_pool/large_pool/java_pool
db_cache:数据库缓存(block buffer)对于oracle数据库的运转和性能起着很是关键的做用,它占据oracle数据库sga
(系统共享内存区)的主要部分。oracle数据库经过使用lru算法,将最近访问的数据块存放到缓存中,从而优化对磁盘
数据的访问.
shared_pool:共享池的大小对于oracle 性能来讲都是很重要的。共享池中保存数据字典高速缓冲和彻底解析或编译的
的pl/SQL 块和SQL 语句及控制结构
large_pool:使用mts配置时,由于要在sga中分配uga来保持用户的会话,就是用large_pool来保持这个会话内存使用
rman作备份的时候,要使用large_pool这个内存结构来作磁盘i/o缓存器
java_pool:为java procedure预备的内存区域,若是没有使用java proc,java_pool不是必须的
oracle系统进程主要有哪些,做用是什么
数据写进程(dbwr):负责将更改的数据从数据库缓冲区高速缓存写入数据文件
日志写进程(lgwr):将重作日志缓冲区中的更改写入在线重作日志文件
系统监控 (smon): 检查数据库的一致性若有必要还会在数据库打开时启动数据库的恢复
进程监控 (pmon): 负责在一个oracle 进程失败时清理资源
检查点进程(ckpt):负责在每当缓冲区高速缓存中的更改永久地记录在数据库中时,更新控制文件和数据文件中的数据库
状态信息。
归档进程 (arch):在每第二天志切换时把已满的日志组进行备份或归档
恢复进程 (reco): 保证分布式事务的一致性,在分布式事务中,要么同时commit,要么同时rollback;
做业调度器(cjq ): 负责将调度与执行系统中已定义好的job,完成一些预约义的工做.
oracle备份分类
逻辑备份:exp/imp 指定表的逻辑备份
物理备份:
热备份:alter tablespace begin/end backup;
冷备份:脱机备份(database shutdown)
rman备份
fullbackup/incremental backup(累积/差别)
物理备份
物理备份是最主要的备份方式。用于保证数据库在最小的数据库丢失或没有数据丢失的状况下获得恢复。
冷物理
冷物理备份提供了最简单和最直接的方法保护数据库因物理损坏丢失。建议在如下几种状况中使用。
对一个已经存在大最数据量的数据库,在晚间数据库能够关闭,此时应用冷物理备份。
对需对数据库服务器进行升级,(如更换硬盘),此时须要备份数据库信息,并在新的硬盘中恢复这些数据信息,建议
采用冷物理备份。
热物理
主要是指备份过程在数据库打开而且用户能够使用的状况下进行。须要执行热物理备份的状况有:
因为数据库性质要求不间断工做,于是此时只能采用热物理备份。
因为备份的要求的时间过长,而数据库只能短期关闭时。
逻辑备份 (exp/imp)
逻辑备份用于实现数据库对象的恢复。但不是基于时间点可彻底恢复的备份策略。只能做为联机备份和脱机备份的一种
补充。
彻底逻辑备份
彻底逻辑备份是将整个数据库导出到一个数据库的格式文件中,该文件能够在不一样的数据库版本、操做系统和硬件平台
之间进行移植。
指定表的逻辑备份
经过备份工具,能够将指定的数据库表备份出来,这能够避免彻底逻辑备份所带来的时间和财力上的浪费。
归档是什么含义
关于归档日志:oracle要将填满的在线日志文件组归档时,则要创建归档日志(archived redo log)。其对数据库备份
和恢复有下列用处:
数据库后备以及在线和归档日志文件,在操做系统和磁盘故障中可保证所有提交的事物可被恢复。
在数据库打开和正常系统使用下,若是归档日志是永久保存,在线后备能够进行和使用。
数据库可运行在两种不一样方式下:noarchivelog方式或archivelog 方式
数据库在noarchivelog方式下使用时,不能进行在线日志的归档,
数据库在archivelog方式下运行,可实施在线日志的归档
归档是归档当前的联机redo日志文件。
svrmgr>alter system archive log current;
数据库只有运行在archivelog模式下,而且可以进行自动归档,才能够进行联机备份。有了联机备份才有可能进行彻底
恢复。
若是一个表在2004-08-04 10:30:00 被drop,在有完善的归档和备份的状况下,如何恢复
9i 新增的flash back 应该能够;
logminer应该能够找出dml。
有完善的归档和备份,先归档当前数据,而后能够先恢复到删除的时间点以前,把drop 的表导出来,而后再恢复到最后
归档时间;
手工拷贝回全部备份的数据文件
SQL〉startup mount;
SQL〉alter database recover automatic until time '2004-08-04:10:30:00';
SQL〉alter database open resetlogs;
rman是什么,有何特色
rman(recoverymanager)是dba的一个重要工具,用于备份、还原和恢复oracle数据库, rman 能够用来备份和恢复数据
库文件、归档日志、控制文件、系统参数文件,也能够用来执行彻底或不彻底的数据库恢复。
rman有三种不一样的用户接口:command line方式、gui 方式(集成在oem 中的备份管理器)、api 方式(用于集成到第
三方的备份软件中)。
具备以下特色:
1)功能相似物理备份,但比物理备份强大n倍;
2)能够压缩空块;
3)能够在块水平上实现增量;
4)能够把备份的输出打包成备份集,也能够按固定大小分割备份集;
5)备份与恢复的过程能够自动管理;
6)能够使用脚本(存在recovery catalog 中)
7)能够作坏块监测
7、 专题研究
重复数据处理
查看重复记录
select * from table_name where id in (select id from table_name groupby id having count(*)>1)
过滤掉全部多余的重复记录
select distinct * from table_name
或 select t.col1,t.col2 from table_name t group by t.col1,t.col2
删除重复记录
单表记录重复
1)delete from table_namewhere id not in (select max(id)from table_name group by col1,col2,...);
2)delete from table_namet where exists(select 1 from table_name where col1=t.col1 and col2=t. col2 and col3=t. col3 and id>t.id);
多表无效记录
1、两张关联表,删除主表中已经在副表中没有的信息
delete from table1 where not exists ( select 1 from table2 wheretable1.field1=table2.field1)
2、包括全部在 tablea 中但不在 tableb和tablec 中的行并消除全部重复行而派生出一个结果表(minus、intersect)
select a from tablea minus(select afrom tableb union select a from tablec)
查询表中列的值重复出现屡次
注:查询表a中存在id重复3次以上的记录
select *from a t where exists (select 1 from a t1 where t.id = t1.idgroup by id having count(id) > 3);
重复数据处理实践
create table emp(
idint not null primary key,
name varchar (25),
age int
);
insert into empvalues(test_sequence.nextval,'zhang1',26);
insert into empvalues(test_sequence.nextval,'zhang2',27);
insert into empvalues(test_sequence.nextval,'zhang3',28);
insert into emp values(test_sequence.nextval,'zhang1',26);
insert into empvalues(test_sequence.nextval,'zhang2',27);
insert into empvalues(test_sequence.nextval,'zhang3',29);
insert into empvalues(test_sequence.nextval,'wang2',26);
insert into emp values(test_sequence.nextval,'wang1',22);
使用不一样的方法查询出重复的数据
--解法一:查询重复记录的一个方法就是分组统计:
select * from emp where name in (select name from emp group by name having count(*)>1);使用in 或者exists
--查询出重复的名称及重复次数
方法1、select sum(1)as sig, name from emp group by name having sum(1) > 1;
方法2、select name, count(*) from empgroup by name having count(*)>1;
count(*)的效率比sum(1)要高
--解法二:若是对每一个名字都和原表进行比较,大于2我的名字与这条记录相同的就是合格的,就有:
select * from emp where ( select count(*)from emp e where e.name = emp.name )>1;
--解法三:若是有另一个名字相同的人工号不与他相同那么这条记录符合要求:
select * from emp where exists (select *from emp e where e.name = emp.name and
e.id<>emp.id);
解法四:思路同解法三
select distinct emp.* from emp inner join emp e onemp.name=e.name and emp.id<>e.id;
查询过滤掉全部多余的重复记录
--解法一:经过distinct、group by过滤重复
select distinct name,age from emp;
或
select name,age from emp group by name,age;
--解法二:使用临时表
--不推荐
select distinct * into #tmp from emp;
delete from emp;
insert into emp select * from #tmp;
--解法三:使用rowid
--高效,name列使用了索引。
select * from emp a where a.rowid = (selectmin(b.rowid) from emp b where a.name = b.name);
--普通
select a.* from emp a,(select min(b.rowid)row_id from emp b group by b.name) b where a.rowid = b.row_id;
删除SQL表中重复的记录
注:除id值不同外其它字段都同样,每两行记录重复
delete from emp t where exists(select 1from emp where name = t.name and age = t.age and t.id < id)
基于oracle数据背景的重复记录删除
综合评价:★★★
(1).在oracle中,每一条记录都有一个rowid,rowid在整个数据库中是惟一的,rowid肯定了每条记录是在oracle中的哪个数据文件、块、行上。
(2).在重复的记录中,可能全部列的内容都相同,但rowid不会相同,因此只要肯定出重复记录中那些具备最大rowid的就能够了,其他所有删除。
重复记录判断的标准是:
c1,c10和c20这三列的值都相同才算是重复记录,而且保存最新的记录。
(1).适用于有大量重复记录的状况(在c1,c10和c20列上建有索引的时候,用如下语句效率会很高):
方法1、delete from cz where(c1,c10,c20) in (select c1,c10,c20 from cz group by c1,c10,c20 havingcount(*)>1) and rowid not in selectmin(rowid) from cz group by c1,c10,c20 having count(*)>1);
方法2、delete from cz where rowid not in(select min(rowid) fromcz group by c1,c10,c20);
(2).适用于有少许重复记录的状况(注意,对于有大量重复记录的状况,用如下语句效率会很低):
方法1、delete from cz a where a.rowid!=(select max(rowid) from cz b wherea.c1=b.c1 and a.c10=b.c10 and a.c20=b.c20);
方法2、delete from cz a where a.rowid<(select max(rowid) from cz b wherea.c1=b.c1 and a.c10=b.c10 and a.c20=b.c20);
方法3、delete from cz a where rowid <(select max(rowid) from cz wherec1=a.c1 and c10=a.c10 and c20=a.c20);
详细教程:http://www.csdn.net/article/2010-08-17/278287
oracle取随机数据
注:随机取出n条数据
1)select * from (select * from tablename order bysys_guid()) where rownum < n;
2)select * from (select * from tablename order by dbms_random.value)where rownum< n
3) select * from (select *from tablename sample(n) order by trunc(dbms_random.value(0,1000))) where rownum < n;
说明: sample(n)含义为检索表中的n%数据,sample值应该在[0.000001,99.999999]之间。
其中 sys_guid() 和 dbms_random.value都是内部函数。
oracle中通常获取随机数的方法是
select trunc(dbms_random.value(0,1000)) from dual; (0-1000的整数)
select dbms_random.value(0, 1000) fromdual; (0-1000的浮点数)
oracle 关键字case的用法
case匹配语句
case expression
when value then statement
[when value then statement ]...
[else statement [, statement ]... ]
end case;
case搜索语句
case
when (boolean_condition1) then action1;
when (boolean_condition2) then action2;
when (boolean_condition3) then action3;
……
else action;
end case;
select case 语句
有一张表,里面有3个字段:语文,数学,英语。其中有3条记录分别表示语文70分,数学80分,英语58分,请用一条SQL语句查询出这三条记录并按如下条件显示出来。大于或等于80表示优秀,大于或等于60表示及格,小于60分表示不及格。
显示格式:
语文 数学 英语
及格 优秀 不及格
------------------------------------------
select
(case when 语文>=80 then '优秀'
when 语文>=60 then '及格'
else '不及格') as 语文,
(case when 数学>=80 then'优秀'
when 数学>=60 then '及格'
else '不及格') as 数学,
(case when 英语>=80 then'优秀'
when 英语>=60 then '及格'
else '不及格') as 英语
from table
oracle日期函数
1、日程安排提早五分钟提醒(oracle 日期函数)
select * from 日程安排 where datediff(‘minute’,开始时间,getdate())>5
2、请取出tb_send表中日期(sendtime字段)为当天的全部记录?(sendtime字段为date型,包含日期与时间)
select * from tb_send t where to_char(t.sendtime,’yyyy-mm-dd’) = to_char(sysdate,’yyyy-mm-dd’) |
oracle trunc()函数的用法
综合评价:★★
trunc() 操做日期
1.select trunc(sysdate) from dual --2011-3-18 今天的日期为2011-3-18 2.select trunc(sysdate, 'mm') from dual --2011-3-1 返回当月第一天. 3.select trunc(sysdate,'yy') from dual --2011-1-1 返回当年第一天 4.select trunc(sysdate,'dd') from dual --2011-3-18 返回当前年月日 5.select trunc(sysdate,'yyyy') from dual --2011-1-1 返回当年第一天 6.select trunc(sysdate,'d') from dual --2011-3-13 (星期天)返回当前星期的第一天 7.select trunc(sysdate, 'hh') from dual --2011-3-18 14:00:00 当前时间为14:41 8.select trunc(sysdate, 'mi') from dual --2011-3-18 14:41:00 trunc()函数没有秒的精确 |
trunc() 操做数字
注:trunc(number,num_digits) number 须要截尾取整的数字。 num_digits 用于指定取整精度的数字。num_digits 的默认值为 0。trunc()函数截取时不进行四舍五入
9.select trunc(123.458) from dual --123 10.select trunc(123.458,0) from dual --123 11.select trunc(123.458,1) from dual --123.4 12.select trunc(123.458,-1) from dual --120 13.select trunc(123.458,-4) from dual --0 |
14.select trunc(123.458,4) from dual --123.458 15.select trunc(123) from dual --123 16.select trunc(123,1) from dual --123 17.select trunc(123,-1) from dual –120 |
计算当月的天数
综合评价:★★
select datepart(dd,dateadd(dd,-1,dateadd(mm,1,cast(cast(year(getdate()) as varchar)+'-'+cast(month(getdate()) as varchar)+'-01' as datetime)))) |
1.求当年天数
selectadd_months(trunc(sysdate, 'yyyy'), 12) - trunc(sysdate, 'yyyy') days from dual
2.求当月天数
selectto_number(to_char(last_day(trunc(sysdate)),'dd')) from dual
3.求指定月天数
select to_number(add_months(trunc(to_date('2013-06-1710:09:02',
'yyyy-fmmm-ddhh24:mi:ss'), 'mm'), 1) - trunc(to_date('2013-06-17 10:09:02',
'yyyy-fmmm-ddhh24:mi:ss'), 'mm')) from dual;
oracle临时表
oracle多行数据分组拼接
只用一条SQL语句,要求从左表查询出右表
lefttable: righttable:
id name id name
---------- ------------------
1 a5 1 a5,a8,af....
2 a8 2 b5,b3,bd....
3 af 3 c3,ck,ci....
4 b5
5 b3
6 bd
7 c3
8 ck
9 ci
解答:
select replace(wmsys.wm_concat(t.name),',',',')from lefttable t group by substr(t.name,0,1);
oracle分析函数
oracle 经常使用函数,nvl、递归、字符串操做、日期操做、数字操做
case when、正则表达式操做
oracle分页与rownum
参照表:test_tb_grade
题目:查询6到10条数据。
解法1、注:采用rownum关键字(三层嵌套)
select * from ( select t.*,rownum rn from (select * from test_tb_grade order by user_id) t where rownum <= 10) where rn >=6; |
解法二、注:采用row_number解析函数进行分页(效率更高)
select * from ( select t.*, row_number() over(order by t.user_id) rn from test_tb_grade t) where rn between 6 and 10; |
行转列,列转行
行转列
create table test_tb_grade -- 学生语文、数学、英语成绩统计
(
user_id number(10) not nullprimary key,
user_name varchar2(20),
course varchar2(20),
score float
);
create sequence test_sequence;
insert into test_tb_gradevalues(test_sequence.nextval,'m','c',78);
insert into test_tb_gradevalues(test_sequence.nextval,'m','m',95);
insert into test_tb_gradevalues(test_sequence.nextval,'m','e',81);
insert into test_tb_gradevalues(test_sequence.nextval,'z','c',97);
insert into test_tb_gradevalues(test_sequence.nextval,'z','m',78);
insert into test_tb_gradevalues(test_sequence.nextval,'z','e',91);
insert into test_tb_gradevalues(test_sequence.nextval,'l','c',80);
insert into test_tb_grade values(test_sequence.nextval,'l','m',55);
insert into test_tb_gradevalues(test_sequence.nextval,'l','e',75);
insert into test_tb_gradevalues(test_sequence.nextval,'v','c',49);
insert into test_tb_gradevalues(test_sequence.nextval,'v','m',63);
insert into test_tb_grade values(test_sequence.nextval,'v','e',70);
insert into test_tb_gradevalues(test_sequence.nextval,'k','e',53);
insert into test_tb_gradevalues(test_sequence.nextval,'k','m',58);
insert into test_tb_gradevalues(test_sequence.nextval,'k','c',59);
insert into test_tb_gradevalues(test_sequence.nextval,'k','c',59);
commit;
列出不一样科目下学生对应的成绩,如图
注:sum汇集函数也能够用max、min、avg等其余汇集函数替代。不一样聚合函数获得结果根据数据会有稍微的差别
select user_name,
sum(decode(course,'c',score,null)) aschinese,
sum(decode(course,'m',score,null)) as math,
sum(decode(course,'e',score,null)) as english
from test_tb_grade
group by user_name
order by 1;
列转行
create table test_tb_grade2 (
gidnumber(10) primary key,
sname varchar2(20),
cn_score float,
math_score float,
en_score float
);
insert into test_tb_grade2 values(test_sequence.nextval,'m',70,65,60);
insert into test_tb_grade2 values(test_sequence.nextval,'l',74,83,76);
insert into test_tb_grade2 values(test_sequence.nextval,'v',80,68,89);
insert into test_tb_grade2 values (test_sequence.nextval,'k',80,83,82);
insert into test_tb_grade2 values(test_sequence.nextval,'z',58,86,80);
commit;
查询结果如图:
须要实现到以下效果:
方法1、union all
select t2.sname,'chinese' as course,t2.cn_score as score from test_tb_grade2 t2 -- 语文
union all
select t2.sname,'math' as course,t2.math_score as score from test_tb_grade2 t2 -- 数学
union all
select t2.sname,'english' as course,t2.en_score as score from test_tb_grade2 t2 -- 英语
方法2、model (复杂)
方法3、collection(涉及到数组、集合、对象)
http://www.itpub.net/forum.php?mod=viewthread&tid=1017026
分组统计与取值
分组统计
实现对各门功课的不一样分数段的学生进行统计(0-60,60-70,70-80..)
方法1、select t2.course,count(t2.course),'0-60'as ts from (
select t.course,t.score from test_tb_gradet group by t.course,t.score having t.score<60 ) t2 group by t2.course
union all
select t2.course,count(t2.course),'60-70'asts from (
select t.course,t.score from test_tb_gradet group by t.course,t.score having t.score>60 and t.score<=70 ) t2 groupby t2.course….
方法2、select t.course,
case
when t.score<= 60 then '0-60'
when 60< t.score and t.score <=70 then '60-70'
when 70< t.score and t.score <=80 then '70-80'
when 80< t.score then '80-100'
else 'error' end as score_cp,
count(t.course)
fromtest_tb_grade t group by t.course
获得以下视图
取前n条记录
参照表:test_tb_grade
题目:查询出每一个学生分数最高的两门课(容许并列第二)
解法1、注:使用左联接和分组函数
select t1.user_id,t1.user_name,t1.course,t1.score from test_tb_grade t1 left
join test_tb_grade t2 on t1.user_name =t2.user_name and t1.score < t2.score
group by t1.user_id,t1.user_name,t1.course,t1.score
having count(t2.user_id) < 2;
解法2、注:oracle分析函数(不出现并列)
select * from (
select user_id, user_name, course, score, row_number()over(partition by user_nameorder by score desc) rn from test_tb_grade) where rn <= 2;
解法3、注:使用关联子查询
select * from test_tb_grade t where 2 > (
select count(*) from test_tb_grade where user_name= t.user_name and t.score < score);
取最大记录
参照表:test_tb_grade
题目:查询每一个单科分数最高的信息
解法1、注:分组函数不能放到where后面
select * from test_tb_grade t where t.score=
( select max(score) from test_tb_gradewhere course = t.course) order by t.user_name;
解法2、注:使用notexists 关键字
select * from test_tb_grade t where not exists
(select 1 from test_tb_grade where course = t. course andt.score < score);
解法3、注:使用内链接查询
select t.* from test_tb_grade t,
(select course,max(score) score fromtest_tb_grade group by course) t1
where t. course = t1. course and t.score =t1.score;
解法4、注:使用关联子查询
select t.* from test_tb_grade t where 1> (select count(*) from test_tb_grade where user_name = t.user_name andt.score < score);
8、 练习题
1、s(s#,sn,sd,sa) s#,sn,sd,sa分别表明学号,学员姓名,所属单位,学员年龄
c(c#,cn) c#,cn分别表明课程编号,课程名称
sc(s#,c#,g) s#,c#,g分别表明学号,所选的课程编号,学习成绩
(1)使用标准SQL嵌套语句查询选修课程名称为’税收基础’的学员学号和姓名?
答案:select s# ,sn from s where s# in(select s# from c,sc wherec.c#=sc.c# and cn=’税收基础’)
selects.s#, s.sn from sc,s,c where c.c# = sc.c# and sc.s# = s.s# and c.cn = ‘税收基础’;
selects#, sn from s where exists (select 1 from c,sc where c.c# = sc.c# and c.cn = ‘税收基础’ andsc.s# = s.s#);
(2) 使用标准SQL嵌套语句查询选修课程编号为’c2’的学员姓名和所属单位?
答:select sn,sd from s,sc where s.s#=sc.s# and sc.c#=’c2’;
(3) 使用标准SQL嵌套语句查询不选修课程编号为’c5’的学员姓名和所属单位?
答:select sn,sd from s where s# not in(select s# from sc where c#=’c5’)
(4)查询选修了课程的学员人数
selectcount(distinct s#) 学员人数 from sc;
(5) 查询选修课程超过5门的学员学号和所属单位
selectsn,sd from s where s# in(select s# from sc group by s# having count(distinctc#)>5)
selects#, sd from s t where exists (select 1 from sc where t.s# = s# group by #having count(distinct c#)>5);
(6)查询a(id,name)表中第31至40条记录,id做为主键多是不是连续增加的列
SQLserver:
select top 10 * from (select top 40 * from a order by id)order by id desc;
select * from a limit 31,40;
oracle:rownum 始终从下一个整数开始,第一值是1,若是前9个值能够肯定,那下一个值就10(rownum 是伪列)
select * from (select t.*, rownum rn from at where rownum <= 40 order by id) t1 where t1.rn => 31;
(7)查询表a中存在id重复三次以上的记录
select *from a t where exists (select 1 from a t1 where t1.id = t.id group by id havingcount(id) > 3);
(8)游标使用
-- 声明游标;CURSOR cursor_name IS select_statement
--For 循环游标
--(1)定义游标
--(2)定义游标变量
--(3)使用for循环来使用这个游标
declare
--类型定义
cursor c_job
is
select empno,ename,job,sal
from emp
where job='MANAGER';
--定义一个游标变量v_cinfoc_emp%ROWTYPE ,该类型为游标c_emp中的一行数据类型
c_row c_job%rowtype;
begin
for c_row in c_job loop
dbms_output.put_line(c_row.empno||'-'||c_row.ename||'-'||c_row.job||'-'||c_row.sal);
end loop;
end;
--Fetch游标
--使用的时候必需要明确的打开和关闭
declare
--类型定义
cursor c_job
is
select empno,ename,job,sal
from emp
where job='MANAGER';
--定义一个游标变量
c_row c_job%rowtype;
begin
open c_job;
loop
--提取一行数据到c_row
fetch c_job into c_row;
--判读是否提取到值,没取到值就退出
--取到值c_job%notfound 是false
--取不到值c_job%notfound 是true
exit when c_job%notfound;
dbms_output.put_line(c_row.empno||'-'||c_row.ename||'-'||c_row.job||'-'||c_row.sal);
end loop;
--关闭游标
close c_job;
end;
--1:任意执行一个update操做,用隐式游标sql的属性%found,%notfound,%rowcount,%isopen观察update语句的执行状况。
begin
update emp set ENAME='ALEARK' WHEREEMPNO=7469;
if sql%isopen then
dbms_output.put_line('Openging');
else
dbms_output.put_line('closing');
end if;
if sql%found then
dbms_output.put_line('游标指向了有效行');--判断游标是否指向有效行
else
dbms_output.put_line('Sorry');
end if;
if sql%notfound then
dbms_output.put_line('Also Sorry');
else
dbms_output.put_line('Haha');
end if;
dbms_output.put_line(sql%rowcount);
exception
when no_data_found then
dbms_output.put_line('Sorry No data');
when too_many_rows then
dbms_output.put_line('Too Many rows');
end;
declare
empNumber emp.EMPNO%TYPE;
empName emp.ENAME%TYPE;
begin
if sql%isopen then
dbms_output.put_line('Cursor is opinging');
else
dbms_output.put_line('Cursor is Close');
end if;
if sql%notfound then
dbms_output.put_line('No Value');
else
dbms_output.put_line(empNumber);
end if;
dbms_output.put_line(sql%rowcount);
dbms_output.put_line('-------------');
select EMPNO,ENAME into empNumber,empName fromemp where EMPNO=7499;
dbms_output.put_line(sql%rowcount);
if sql%isopen then
dbms_output.put_line('Cursor is opinging');
else
dbms_output.put_line('Cursor is Closing');
end if;
if sql%notfound then
dbms_output.put_line('No Value');
else
dbms_output.put_line(empNumber);
end if;
exception
when no_data_found then
dbms_output.put_line('No Value');
when too_many_rows then
dbms_output.put_line('too many rows');
end;
--2,使用游标和loop循环来显示全部部门的名称
--游标声明
declare
cursor csr_dept
is
--select语句
select DNAME
from Depth;
--指定行指针,这句话应该是指定和csr_dept行类型相同的变量
row_dept csr_dept%rowtype;
begin
--for循环
for row_dept in csr_dept loop
dbms_output.put_line('部门名称:'||row_dept.DNAME);
end loop;
end;
--3,使用游标和while循环来显示全部部门的的地理位置(用%found属性)
declare
--游标声明
cursor csr_TestWhile
is
--select语句
select LOC
from Depth;
--指定行指针
row_loc csr_TestWhile%rowtype;
begin
--打开游标
open csr_TestWhile;
--给第一行喂数据
fetch csr_TestWhile into row_loc;
--测试是否有数据,并执行循环
while csr_TestWhile%found loop
dbms_output.put_line('部门地点:'||row_loc.LOC);
--给下一行喂数据
fetch csr_TestWhile into row_loc;
end loop;
close csr_TestWhile;
end;
select *from emp
--4,接收用户输入的部门编号,用for循环和游标,打印出此部门的全部雇员的全部信息(使用循环游标)
--CURSORcursor_name[(parameter[,parameter],...)] IS select_statement;
--定义参数的语法以下:Parameter_name [IN] data_type[{:=|DEFAULT} value]
declare
CURSOR
c_dept(p_deptNo number)
is
select * from emp where emp.depno=p_deptNo;
r_emp emp%rowtype;
begin
for r_emp in c_dept(20) loop
dbms_output.put_line('员工号:'||r_emp.EMPNO||'员工名:'||r_emp.ENAME||'工资:'||r_emp.SAL);
end loop;
end;
select *from emp
--5:向游标传递一个工种,显示此工种的全部雇员的全部信息(使用参数游标)
declare
cursor
c_job(p_job nvarchar2)
is
select * from emp where JOB=p_job;
r_job emp%rowtype;
begin
for r_job in c_job('CLERK') loop
dbms_output.put_line('员工号'||r_job.EMPNO||' '||'员工姓名'||r_job.ENAME);
end loop;
end;
SELECT *FROM EMP
--6:用更新游标来为雇员加佣金:(用if实现,建立一个与emp表一摸同样的emp1表,对emp1表进行修改操做),并将更新先后的数据输出出来
--http://zheng12tian.iteye.com/blog/815770
create table emp1 as select * from emp;
declare
cursor
csr_Update
is
select * from emp1 for update OF SAL;
empInfo csr_Update%rowtype;
saleInfo emp1.SAL%TYPE;
begin
FOR empInfo IN csr_Update LOOP
IF empInfo.SAL<1500 THEN
saleInfo:=empInfo.SAL*1.2;
elsif empInfo.SAL<2000 THEN
saleInfo:=empInfo.SAL*1.5;
elsif empInfo.SAL<3000 THEN
saleInfo:=empInfo.SAL*2;
END IF;
UPDATE emp1 SET SAL=saleInfo WHERE CURRENT OFcsr_Update;
END LOOP;
END;
--7:编写一个PL/SQL程序块,对名字以‘A’或‘S’开始的全部雇员按他们的基本薪水(sal)的10%给他们加薪(对emp1表进行修改操做)
declare
cursor
csr_AddSal
is
select * from emp1 where ENAME LIKE 'A%' ORENAME LIKE 'S%' for update OF SAL;
r_AddSal csr_AddSal%rowtype;
saleInfo emp1.SAL%TYPE;
begin
for r_AddSal in csr_AddSal loop
dbms_output.put_line(r_AddSal.ENAME||'原来的工资:'||r_AddSal.SAL);
saleInfo:=r_AddSal.SAL*1.1;
UPDATE emp1 SET SAL=saleInfo WHERE CURRENT OFcsr_AddSal;
end loop;
end;
--8:编写一个PL/SQL程序块,对全部的salesman增长佣金(comm)500
declare
cursor
csr_AddComm(p_job nvarchar2)
is
select * from emp1 where JOB=p_job FOR UPDATEOF COMM;
r_AddComm emp1%rowtype;
commInfo emp1.comm%type;
begin
for r_AddComm in csr_AddComm('SALESMAN') LOOP
commInfo:=r_AddComm.COMM+500;
UPDATE EMP1 SET COMM=commInfo where CURRENT OFcsr_AddComm;
END LOOP;
END;
--9:编写一个PL/SQL程序块,以提高2个资格最老的职员为MANAGER(工做时间越长,资格越老)
--(提示:能够定义一个变量做为计数器控制游标只提取两条数据;也能够在声明游标的时候把雇员中资格最老的两我的查出来放到游标中。)
declare
cursor crs_testComput
is
select * from emp1 order by HIREDATE asc;
--计数器
top_two number:=2;
r_testComput crs_testComput%rowtype;
begin
open crs_testComput;
FETCH crs_testComput INTO r_testComput;
while top_two>0 loop
dbms_output.put_line('员工姓名:'||r_testComput.ENAME||' 工做时间:'||r_testComput.HIREDATE);
--计速器减一
top_two:=top_two-1;
FETCH crs_testComput INTO r_testComput;
end loop;
close crs_testComput;
end;
--10:编写一个PL/SQL程序块,对全部雇员按他们的基本薪水(sal)的20%为他们加薪,
--若是增长的薪水大于300就取消加薪(对emp1表进行修改操做,并将更新先后的数据输出出来)
declare
cursor
crs_UpadateSal
is
select * from emp1 for update of SAL;
r_UpdateSal crs_UpadateSal%rowtype;
salAdd emp1.sal%type;
salInfo emp1.sal%type;
begin
for r_UpdateSal in crs_UpadateSal loop
salAdd:= r_UpdateSal.SAL*0.2;
if salAdd>300 then
salInfo:=r_UpdateSal.SAL;
dbms_output.put_line(r_UpdateSal.ENAME||': 加薪失败。'||'薪水维持在:'||r_UpdateSal.SAL);
else
salInfo:=r_UpdateSal.SAL+salAdd;
dbms_output.put_line(r_UpdateSal.ENAME||': 加薪成功.'||'薪水变为:'||salInfo);
end if;
update emp1 set SAL=salInfo where current ofcrs_UpadateSal;
end loop;
end;
--11:将每位员工工做了多少年零多少月零多少天输出出来
--近似
--CEIL(n)函数:取大于等于数值n的最小整数
--FLOOR(n)函数:取小于等于数值n的最大整数
--truc的用法http://publish.it168.com/2005/1028/20051028034101.shtml
declare
cursor
crs_WorkDay
is
select ENAME,HIREDATE,trunc(months_between(sysdate, hiredate) / 12) AS SPANDYEARS,
trunc(mod(months_between(sysdate, hiredate),12)) AS months,
trunc(mod(mod(sysdate - hiredate, 365), 12))as days
from emp1;
r_WorkDay crs_WorkDay%rowtype;
begin
for r_WorkDay in crs_WorkDay loop
dbms_output.put_line(r_WorkDay.ENAME||'已经工做了'||r_WorkDay.SPANDYEARS||'年,零'||r_WorkDay.months||'月,零'||r_WorkDay.days||'天');
end loop;
end;
--12:输入部门编号,按照下列加薪比例执行(用CASE实现,建立一个emp1表,修改emp1表的数据),并将更新先后的数据输出出来
--deptno raise(%)
-- 10 5%
-- 2010%
-- 3015%
-- 4020%
-- 加薪比例以现有的sal为标准
--CASEexpr WHEN comparison_expr THEN return_expr
--[,WHEN comparison_expr THEN return_expr]... [ELSE else_expr] END
declare
cursor
crs_caseTest
is
select * from emp1 for update of SAL;
r_caseTest crs_caseTest%rowtype;
salInfo emp1.sal%type;
begin
for r_caseTest in crs_caseTest loop
case
when r_caseTest.DEPNO=10
THEN salInfo:=r_caseTest.SAL*1.05;
when r_caseTest.DEPNO=20
THEN salInfo:=r_caseTest.SAL*1.1;
when r_caseTest.DEPNO=30
THEN salInfo:=r_caseTest.SAL*1.15;
when r_caseTest.DEPNO=40
THEN salInfo:=r_caseTest.SAL*1.2;
end case;
update emp1 set SAL=salInfo where current ofcrs_caseTest;
end loop;
end;
--13:对每位员工的薪水进行判断,若是该员工薪水高于其所在部门的平均薪水,则将其薪水减50元,输出更新先后的薪水,员工姓名,所在部门编号。
--AVG([distinct|all]expr) over (analytic_clause)
---做用:
--按照analytic_clause中的规则求分组平均值。
--分析函数语法:
--FUNCTION_NAME(,...)
--OVER
--()
--PARTITION子句
--按照表达式分区(就是分组),若是省略了分区子句,则所有的结果集被看做是一个单一的组
select * from emp1
DECLARE
CURSOR
crs_testAvg
IS
select EMPNO,ENAME,JOB,SAL,DEPNO,AVG(SAL) OVER(PARTITION BY DEPNO ) AS DEP_AVG
FROM EMP1 for update of SAL;
r_testAvg crs_testAvg%rowtype;
salInfo emp1.sal%type;
begin
for r_testAvg in crs_testAvg loop
if r_testAvg.SAL>r_testAvg.DEP_AVG then
salInfo:=r_testAvg.SAL-50;
end if;
update emp1 set SAL=salInfo where current ofcrs_testAvg;
end loop;
end;
9、 存在的问题
1) 存储过程、游标使用
2) Oracle经常使用函数、分析函数、oracle 递归查询
3) 基于oracle的SQL优化
4) 数据库设计原则、设计规范学习
5) 教材:数据库设计与开发、数据库设计入门。。。。