一、增删改数据
1、向emp表中插入一条记录
① (方式一:按表默认结构顺序)insert into 表名 values ...语法
insert into emp values(1111,'JACK','IT',7788,sysdate,1000,100,40);
② (方式二:按自定义顺序)insert into 表名(列名) values ...语法
insert into emp(ENAME,EMPNO,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO)
values('MARRY',2222,'IT',7788,sysdate,1000,100,40);
2、向emp表中插入NULL值,前提是所插入的字段允许插入NULL值
① (方式一:采用显示插入NULL值)
insert into emp values(3333,'SISI','IT',7788,sysdate,1000,NULL,40);
② (方式二:采用隐式插入NULL值)
insert into emp(ENAME,EMPNO,JOB,MGR,HIREDATE,SAL,DEPTNO)
values('SOSO',4444,'IT',7788,sysdate,1000,40);
3、使用&
占位符,动态输入值,&可以运用在任何一个DML语句中
① 在values子句中使用
insert into emp values(&empno,'&ename','&job',&mgr,&hiredate,&sal,&comm,&xxxxxxxx);
注意:&
是sqlplus工具提供的占位符,如果是字符串或日期型要加''
符,数值型无需加''
符
② 在from子句中使用
select * from &table;
③ 在select子句中使用
select empno,ename,&colname from emp;
④ 在where子句中使用
select * from emp where sal > &money;
⑤ 在group by 和 having子句中使用
select deptno,avg(sal)
from emp
group by &deptno
having avg(sal) > &money;
4、删除emp表中的所有记录
delete from emp;
5、将xxx_emp表中所有20号部门的员工,复制到emp表中【批量插入】insert into 表名 select ...语法
insert into emp
select *
from xxx_emp
where deptno=20;
6、将’SMITH’的工资增加20%
update emp set sal=sal*1.2 where ename = upper('smith');
7、将’SMITH’的工资设置为20号部门的平均工资,这是一个条件未知的事物,优先考虑子查询
第一:20号部门的平均工资
select avg(sal) from emp where deptno=20;
第二:将’SMITH’的工资设置为2207
update emp set sal=2207 where ename = 'SMITH';
子查询:
update emp
set sal = (
select avg(sal)
from emp
where deptno=20
)
where ename = 'SMITH';
8、删除工资比所有部门平均工资都低的员工,这是一个条件未知的事物,优先考虑子查询
delete
from emp
where sal < all(
select avg(sal)
from emp
group by deptno
);
9、删除无佣金的员工
delete from emp where comm is null;
10、将emp表丢入回收站,drop table 表名
drop table emp;
11、从回收站将emp表闪回,flashback table 表名 to before drop
flashback table emp to before drop;
12、查询回收站,show recyclebin
show recyclebin;
13、清空回收站,purge recyclebin
purge recyclebin;
14、使用关键字purge,彻底删除emp表,即不会将emp表丢入回收站,永久删除emp表,drop table 表名 purge
drop table emp purge;
15、依据xxx_emp表结构,创建emp表的结构,但不会插入数据
create table emp
as
select * from xxx_emp where 1<>1;
16、创建emp表,复制xxx_emp表中的结构,同时复制xxx_emp表的所有数据
create table emp
as
select * from xxx_emp where 1=1;
注意:where不写的话,默认为true
17、将emp截断,再自动创建emp表,truncate table 表名
truncate table emp;
18、向emp表,批量插入来自xxx_emp表中部门号为20的员工信息,只包括empno,ename,job,sal字段
insert into emp(empno,ename,job,sal)
select empno,ename,job,sal
from xxx_emp
where deptno=20;
19、使用关键字purge,彻底删除emp表,即不会将emp表丢入回收站
drop table emp purge;
20、依据xxx_emp表,只创建emp表,但不复制数据,且emp表只包括empno,ename字段
create table emp(empno,ename)
as
select empno,ename from xxx_emp where 1=2;
21、向emp表(只含有empno和ename字段),批量插入xxx_emp表中部门号为20的员工信息
insert into emp(empno,ename)
select empno,ename from xxx_emp where deptno=20;
22、drop table 和 truncate table 和 delete from 区别:
drop table | truncate table | delete from |
---|---|---|
属于DDL | 属于DDL | 属于DML |
不可回滚 | 不可回滚 | 可回滚 |
不可带where | 不可带where | 可带where |
表内容和结构删除 | 表内容删除 | 表结构在,表内容要看where执行的情况 |
删除速度最快 | 删除速度快 | 删除速度慢,需要逐行删除 |
二、事务
1、什么是事务?
一个不可分割的子操作形成一个整体,该整体要么全部执行成功,要么全部执行失败。例如:转帐
2、为什么要用事务?
如果不用事务的话,为转帐为例,可能出现一个用户钱增加了,另一个用户钱不变
3、编程中,事务可用于哪一层?
事务放在业务层
4、jdbc编程中,如何使用事务?
connection.setAutoCommit(false);
pstmt.executeUpdate();
connection.commit();
connection.rollback();
5、hibernate编程中,如何使用事务?
transaction.begin();
session.save(new User());
transaction.commit();
transaction.rollback();
6、spring编程中,如何使用事务?
spring可以分为二种
① 编程式事务:藕合
② 声明式事务:解藕【提倡】
7、Oracle的事务只针对DML操作,即select/insert/update/delete
① MySQL的事务开始:start transaction
② Oracle的事务开始:第一条DML操作做为事务开始
8、Oracle的提交事务
① 显示提交:commit
② 隐藏提交:DDL/DCL/exit(sqlplus工具)
注意:提交是的从事务开始到事务提交中间的内容,提交到ORCL数据库中的DBF二进制文件
9、Oracle的回滚事务
① 显示回滚:rollback
② 隐藏回滚:关闭窗口(sqlplus工具),死机,掉电
注意:回滚到事务开始的地方
10、什么是回滚点?
在操作之间设置的一个标志位,用于将来回滚之用
11、为什么要设置回滚点?savepoint a;rollback to savepoint a;
如果没有设置回滚点的话,Oracle必须回滚到事务开始的地方,其间做的一个正确的操作也将撤销
12、使用savepoint 回滚点,设置回滚点a
savepoint a;
13、使用rollback to savepoint
,回滚到回滚点a处
rollback to savepoint a;
14、Oracle提交或回滚后,原来设置的回滚点还有效吗?
原回滚点无效了
15、Oracle之所以能回滚的原因是?
主要机制是实例池
16、MySQL支持的四种事务隔离级别及能够解决的问题
① read uncommitted:不能解决任何缺点
② read committed:脏读,Oracle默认
③ reapatable read:不可重复读,脏读,MySQL默认
④ serializable:幻读,不可重复读,脏读,效率低
注意:jdbc/dbutils速度快,但书写烦;mybaits速度中等,但书写"中等"; hibernate速度慢,但书写"爽"
17、Oracle支持的二种事务隔离级别及能够解决的问题
Oracle支持:read committed 和 serializable
18、Oracle中设置事务隔离级别为serializable
set transaction isolation level serializable;
19、二个用户同时操作emp表,删除KING这条记录会有什么后果?
因为有隔离级别的存在,所以不会出现二个用户都删除了KING这条记录,
一定是一个用户删除KING成功,在该用户没有提交的情况下,另一个用户等待
三、用户权限控制:访问其它用户下的对象
1、查询当前用户是谁
show user;
2、查询scott自己表空间下的所有对象时,可加,或不加用户名
select * from emp;
或 select * from scott.emp;
3、以sysdba身份解锁hr普通帐户
alter user hr account unlock;
4、以sysdba身份设置hr普通帐户的密码
alter user hr identified by lion;
5、当scott查询hr表空间下的所有表时,必须得加用户名
select * from hr.jobs;
6、以sysdba身份角色,授予scott用户查询所有用户空间下的对象权限
grant select any table to scott;
在默认情况下,每个用户只能查询自已空间下的对象的权限,不能查询其它用户空间下的对象
7、以sysdba身份,撤销scott用户查询所有用户空间下的对象权限
revoke select any table from scott;
8、scott自已查看自己所拥有的权限
select * from user_sys_privs;
9、从scott用户空间导航到sysdba用户空间
conn / as sysdba;
10、从sysdba用户空间导航到scott用户空间
conn scott/tiger;
11、从scott用户空间导航到hr用户空间
conn hr/lion;
12、查询hr用户空间中的所有对象
select * from tab;
13、在scott用户空间下,查询hr用户空间下的jobs表,必须加上hr用户空间名
select * from hr.jobs;
四、视图【View】
1、什么是视图
① 视图是一种虚表
② 视图建立在已有表的基础上, 视图赖以建立的这些表称为基表
③ 向视图提供数据内容的语句为 SELECT 语句,可以将视图理解为存储起来的 SELECT 语句
④ 视图向用户提供基表数据的另一种表现形式
⑤ 视图没有存储真正的数据,真正的数据还是存储在基表中
⑥ 程序员虽然操作的是视图,但最终视图还会转成操作基表
⑦ 一个基表可以有0个或多个视图
2、什么情况下会用到视图
① 如果你不想让用户看到所有数据(字段,记录),只想让用户看到某些的数据时,此时可以使用视图
② 当你需要减化SQL查询语句的编写时,可以使用视图,但不提高查询效率
3、视图的作用
① 限制数据访问
② 简化复杂查询
③ 提供数据的相互独立
④ 同样的数据,可以有不同的显示方式
4、基于emp表所有列,创建视图emp_view_1
create view emp_view_1
as
select * from emp;
(默认情况下,普通用户无权创建视图,得让sysdba为你分配creare view的权限 )
① 以sysdba身份,授权scott用户create view权限
grant create view to scott;
② 以sysdba身份,撤销scott用户create view权限
revoke create view from scott;
5、基于emp表指定列,创建视图emp_view_2,该视图包含编号/姓名/工资/年薪/年收入(查询中使用列别名)
create view emp_view_2
as
select empno "编号",ename "姓名",sal "工资",sal*12 "年薪",sal*12+NVL(comm,0) "年收入"
from emp;
6、基于emp表指定列,创建视图emp_view_3(a,b,c,d,e),包含编号/姓名/工资/年薪/年收入(视图中使用列名)
create view emp_view_3(a,b,c,d,e)
as
select empno "编号",ename "姓名",sal "工资",sal*12 "年薪",sal*12+NVL(comm,0) "年收入"
from emp;
① 查询emp_view_3创建视图的结构
desc emp_view_3;
② 修改emp_view_3(id,name,salary,annual,income)视图
create or replace view emp_view_3(id,name,salary,annual,income)
as
select empno "编号",ename "姓名",sal "工资",sal*12 "年薪",sal*12+NVL(comm,0) "年收入"
from emp;
7、查询emp表,求出各部门的最低工资,最高工资,平均工资
select min(sal),max(sal),round(avg(sal),0),deptno
from emp
group by deptno;
创建视图emp_view_4,视图中包含各部门的最低工资,最高工资,平均工资
create or replace view emp_view_4
as
select deptno "部门号",min(sal) "最低工资",max(sal) "最高工资",round(avg(sal),0) "平均工资"
from emp
group by deptno;
8、创建视图emp_view_5,视图中包含员工编号,姓名,工资,部门名,工资等级
create or replace view emp_view_5
as
select e.empno "编号",e.ename "姓名",e.sal "工资",d.dname "部门名",s.grade "工资等级"
from emp e,dept d,salgrade s
where (e.deptno=d.deptno) and (e.sal between s.losal and s.hisal);
9、删除视图emp_view_1中的7788号员工的记录,使用delete操作,会影响基表吗
答:delete from emp_view_1 where empno=7788;
写法正确,会影响基表
10、修改emp_view_1为只读视图【with read only】,再执行上述delete操作,还行吗?
create or replace view emp_view_1
as
select * from emp
with read only;
答:不能进行delete操作了
10、删除视图中的【某条】记录会影响基表吗?
答:会影响基表
12、将【整个】视图删除,会影响表吗?
答:不会影响基表
13、删除视图,会进入回收站吗?
答:不会进入回收站
14、删除基表会影响视图吗?
答:会影响视图
15、闪回基表后,视图有影响吗?
答:视图又可以正常工作了
五、同义词【Synonym】
1、什么是同义词
对一些比较长名字的对象(表,视图,索引,序列……)做减化,用别名替代
2、同义词的作用
① 缩短对象名字的长度
② 方便访问其它用户的对象
3、以sys身份授予scott普通用户create synonym权限
grant create synonym to scott;
4、以sys身份从scott普通用户撤销create synonym权限
revoke create synonym from scott;
5、创建与salgrade表对应的同义词
create synonym e for salgrade;
create synonym ev5 for emp_view_5;
6、使用同义词操作salgrade表
select * from s;
7、删除同义词
drop synonym ev5;
8、删除同义词,会影响基表吗?
不会影响基表
9、删除基表,会影响同义词吗?
会影响同义词
六、序列【Sequence】
1、什么是序列
① 类似于MySQL中的auto_increment自动增长机制,但Oracle中无auto_increment机制
② 是oracle提供的一个产生唯一数值型值的机制
③ 通常用于表的主健值
④ 序列只能保证唯一,不能保证连续
声明:oracle中,只有rownum永远保持从1开始,且继续
⑤ 序列值,可放于内存,取之较快
2、题问:为什么Oracle不直接用rownum做主健呢?
rownum=1这条记录不能永远唯一表示SMITH这个用户,但主键=1确可以永远唯一表示SMITH这个用户
3、为什么要用序列
① 以前我们为主健设置值,需要人工设置值,容易出错
② 以前每张表的主健值,是独立的,不能共享
4、为emp表的empno字段,创建序列emp_empno_seq,create sequence 序列名
create sequence emp_empno_seq;
5、删除序列emp_empno_seq,drop sequence 序列名
drop sequence emp_empno_seq;
6、查询emp_empno_seq序列的当前值currval和下一个值nextval,第一次使用序列时,必须选用:序列名.nextval
select emp_empno_seq.nextval from dual;
(首次使用)
select emp_empno_seq.currval from dual;
7、使用序列,向emp表插入记录,empno字段使用序列值
insert into emp(empno) values(emp_empno_seq.nextval);
insert into emp(empno) values(emp_empno_seq.nextval);
insert into emp(empno) values(emp_empno_seq.nextval);
8、修改emp_empno_seq序列的increment by属性为20,默认start with是1
alter sequence emp_empno_seq
increment by 20;
9、修改修改emp_empno_seq序列的的increment by属性为5
alter sequence emp_empno_seq
increment by 5;
10、修改emp_empno_seq序列的start with属性,行吗?不可以
alter sequence emp_empno_seq
start with 100;
11、删除表,会影响序列吗?
你无法做insert操作
12、删除序列,会影响表吗?
表真正亡,序列亡
七、索引【Index】
1、什么是索引
① 是一种快速查询表中内容的机制,类似于新华字典的目录
② 运用在表中某个/些字段上,但存储时,独立于表之外
2、为什么要用索引
① 通过指针加速Oracle服务器的查询速度
② 通过rowid快速定位数据的方法,减少磁盘I/O(rowid是oracle中唯一确定每张表不同记录的唯一身份证)
3、rowid的特点
① 位于每个表中,但表面上看不见,例如:desc emp是看不见的
② 只有在select中,显示写出rowid,方可看见
③ 它与每个表绑定在一起,表亡,该表的rowid亡,二张表rownum可以相同,但rowid必须是唯一的
④ rowid是18位大小写加数字混杂体,唯一表代该条记录在DBF文件中的位置
⑤ rowid可以参与=/like比较时,用’'单引号将rowid的值包起来,且区分大小写
⑥ rowid是联系表与DBF文件的桥梁
4、索引的特点
① 索引一旦建立, Oracle管理系统会对其进行自动维护, 而且由Oracle管理系统决定何时使用索引
② 用户不用在查询语句中指定使用哪个索引
③ 在定义primary key或unique约束后系统自动在相应的列上创建索引
④ 用户也能按自己的需求,对指定单个字段或多个字段,添加索引
5、什么时候【要】创建索引
① 表经常进行 SELECT 操作
② 表很大(记录超多),记录内容分布范围很广
③ 列名经常在 WHERE 子句或连接条件中出现
注意:符合上述某一条要求,都可创建索引,创建索引是一个优化问题,同样也是一个策略问题
6、什么时候【不要】创建索引
① 表经常进行 INSERT/UPDATE/DELETE 操作
② 表很小(记录超少)
③ 列名不经常作为连接条件或出现在 WHERE 子句中
同上注意
7、为emp表的empno单个字段,创建索引emp_empno_idx,叫单列索引
create index emp_empno_idx
on emp(empno);
8、为emp表的ename,job多个字段,创建索引emp_ename_job_idx,多列索引/联合索引
create index emp_ename_job
on emp(ename,job);
注意:如果在where中只出现job不使用索引;如果在where中只出现ename使用索引
注意:索引创建后,只有查询表有关,和其它(insert/update/delete)无关,解决速度问题
9、删除emp_empno_idx和emp_ename_job_idx索引
drop index emp_empno_idx;
drop index emp_ename_job_idx;
八、PLSQL程序设计
1、PLSQL是什么
① 是专用于Oracle服务器,在SQL基础之上添加了一些过程化控制语句
② 过程化包括有:类型定义、判断、循环、游标、异常或例外处理
③ PLSQL强调过程
2、为什么要用PLSQL
因为SQL是第四代命令式语言,无法显示处理过程化的业务,所以得用一个过程化程序设计语言来弥补SQL的不足之处,SQL和PLSQL不是替代关系,是弥补关系
3、PLSQL程序的完整组成结构
[declare]
变量声明;
变量声明;
begin
DML/TCL操作;
DML/TCL操作;
[exception]
例外处理;
例外处理;
end;
/
注意:在PLSQL程序中,;
号表示每条语句的结束,/
表示整个PLSQL程序结束
4、书写PLSQL的工具
① SQLPLUS工具
② SQLDeveloper工具
③ 第三方工具(PLSQL & 其它)
5、PLSQL与SQL执行有什么不同
① SQL是单条执行的
② PLSQL是整体执行的,不能单条执行,整个PLSQL结束用/
,其中每条语句结束用;
号
6、设置显示PLSQL程序的执行结果,默认情况下不显示PLSQL程序的执行结果,语法:set serveroutput on/off;
7、写一个PLSQL程序,输出"hello world"字符串,语法:dbms_output.put_line('需要输出的字符串');
begin
--向SQLPLUS客户端工具输出字符串
dbms_output.put_line('hello 你好');
end;
/
注意:dbms_output
是oracle中的一个输出对象,put_line
是上述对象的一个方法,用于输出一个字符串自动换行
8、使用基本类型变量、常量和注释,求10+100的和
declare
--定义变量
mysum number(3) := 0;
tip varchar2(10) := '结果是';
begin
/*业务算法*/
mysum := 10 + 100;
/*输出到控制器*/
dbms_output.put_line(tip || mysum);
end;
/
9、输出7369号员工姓名和工资,格式如下:7369号员工的姓名是SMITH,薪水是800,语法:使用表名.字段%type
declare
--定义二个变量,分别装姓名和工资
pename emp.ename%type;
psal emp.sal%type;
begin
--SQL语句
--select ename,sal from emp where empno = 7369;
--PLSQL语句,将ename的值放入pename变量中,sal的值放入psal变量中
select ename,sal into pename,psal from emp where empno = 7369;
--输出
dbms_output.put_line('7369号员工的姓名是'||pename||',薪水是'||psal);
end;
/
10、输出7788号员工姓名和工资,格式如下:7788号员工的姓名是SMITH,薪水是3000,语法:使用表名%rowtype
declare
emp_record emp%rowtype;
begin
select * into emp_record from emp where empno = 7788;
dbms_output.put_line('7788号员工的姓名是'||emp_record.ename||',薪水是'||emp_record.sal);
end;
/
疑问:何时使用%type,何时使用%rowtype?【项目中常用%type
】
①当定义变量时,该变量的类型与表中某字段的类型相同时,可以使用%type
② 当定义变量时,该变量与整个表结构完全相同时,可以使用%rowtype
,此时通过变量名.字段名,可以取值变量中对应的值
7、PLSQL判断
① 使用if-else-end if显示今天星期几,是"工作日"还是"休息日"
declare
pday varchar2(10);
begin
select to_char(sysdate,'day') into pday from dual;
dbms_output.put_line('今天是'||pday);
if pday in ('星期六','星期日') then
dbms_output.put_line('休息日');
else
dbms_output.put_line('工作日');
end if;
end;
/
② 用键盘接收值,使用if-elsif-else-end if显示"age<16",“age<30”,“age<60”,“age<80”
declare
age number(3) := &age;
begin
if age < 16 then
dbms_output.put_line('你是未成人');
elsif age < 30 then
dbms_output.put_line('你是青年人');
elsif age < 60 then
dbms_output.put_line('你是奋斗人');
elsif age < 80 then
dbms_output.put_line('你是享受人');
else
dbms_output.put_line('未完再继');
end if;
end;
/
8、PLSQL循环
① 使用loop循环显示1-10
declare
i number(2) := 1;
begin
loop
--当i>10时,退出循环
exit when i>10;
--输出i的值
dbms_output.put_line(i);
--变量自加
i := i + 1;
end loop;
end;
/
② 使用while循环显示1-10
declare
i number(2) := 1;
begin
while i<11
loop
dbms_output.put_line(i);
i := i + 1;
end loop;
end;
/
③ 使用while循环,向emp表中插入999条记录
declare
i number(4) := 1;
begin
while( i < 1000 )
loop
insert into emp(empno,ename) values(i,'哈哈');
i := i + 1;
end loop;
end;
/
④ 使用while循环,从emp表中删除999条记录
declare
i number(4) := 1;
begin
while i<1000
loop
delete from emp where empno = i;
i := i + 1;
end loop;
end;
/
⑤ 使用for循环显示20-30
declare
i number(2) := 20;
begin
for i in 20 .. 30
loop
dbms_output.put_line(i);
end loop;
end;
/
9、PLSQL光标/游标/cursor
类似于JDBC中的ResultSet对象的功能,从上向下依次获取每一记录的内容
① 使用无参光标cursor,查询所有员工的姓名和工资【如果需要遍历多条记录时,使用光标cursor,无记录找到使用cemp%notfound】
declare
--定义游标
cursor cemp is select ename,sal from emp;
--定义变量
vename emp.ename%type;
vsal emp.sal%type;
begin
--打开游标,这时游标位于第一条记录之前
open cemp;
--循环
loop
--向下移动游标一次
fetch cemp into vename,vsal;
--退出循环,当游标下移一次后,找不到记录时,则退出循环
exit when cemp%notfound;
--输出结果
dbms_output.put_line(vename||'--------'||vsal);
end loop;
--关闭游标
close cemp;
end;
/
② 使用带参光标cursor,查询10号部门的员工姓名和工资
declare
cursor cemp(pdeptno emp.deptno%type) is select ename,sal from emp where deptno=pdeptno;
pename emp.ename%type;
psal emp.sal%type;
begin
open cemp(&deptno);
loop
fetch cemp into pename,psal;
exit when cemp%notfound;
dbms_output.put_line(pename||'的薪水是'||psal);
end loop;
close cemp;
end;
/
③ 使用无参光标cursor,真正给员工涨工资(ANALYST涨1000,MANAGER涨800,其它涨400),要求显示编号,姓名,职位,薪水
declare
cursor cemp is select empno,ename,job,sal from emp;
pempno emp.empno%type;
pename emp.ename%type;
pjob emp.job%type;
psal emp.sal%type;
begin
open cemp;
loop
fetch cemp into pempno,pename,pjob,psal;
--循环退出条件一定要写
exit when cemp%notfound;
if pjob='ANALYST' then
update emp set sal = sal + 1000 where empno = pempno;
elsif pjob='MANAGER' then
update emp set sal = sal + 800 where empno = pempno;
else
update emp set sal = sal + 400 where empno = pempno;
end if;
end loop;
commit;
close cemp;
end;
/
10、PLSQL例外
① 使用Oracle系统内置例外,演示除0例外【zero_divide】
declare
myresult number;
begin
myresult := 1/0;
dbms_output.put_line(myresult);
exception
when zero_divide then
dbms_output.put_line('除数不能为0');
end;
/
② 使用Oracle系统内置例外,查询100号部门的员工姓名,演示没有找到数据【no_data_found】
declare
pename varchar2(20);
begin
select ename into pename from emp where deptno = 100;
dbms_output.put_line(pename);
exception
when NO_DATA_FOUND then
dbms_output.put_line('查无该部门员工');
insert into emp(empno,ename) values(1111,'ERROR');
end;
/
③ 使用用户自定义例外,使用光标cursor,查询10/20/30/100号部门的员工姓名,演示没有找到数据【nohave_emp_found】
declare
--游标、例外、变量
cursor cemp(pdeptno number) is select ename from emp where deptno=pdeptno ;
nohave_emp_found exception;
pename emp.ename%type;
begin
--打开游标,这时游标定位于第一条记录之前
open cemp(&xxx);
--向下移动游标一次指向第一条记录
fetch cemp into pename;
--判断
if cemp%notfound then
--抛异常
raise nohave_emp_found;
else
--输出变量pename的值
dbms_output.put_line(pename);
--循环
loop
--向下移动游标一次
fetch cemp into pename;
--如果找不到记录就退出
exit when cemp%notfound;
--输出变量pename的值
dbms_output.put_line(pename);
end loop;
end if;
close cemp;
exception
when nohave_emp_found then
dbms_output.put_line('查无该部门员工');
end;
/
九、存储过程(procedure)与存储函数(function)
1、为什么要用存储过程?
① PLSQL每次执行都要整体运行一遍,才有结果
② PLSQL不能将其封装起来,长期保存在oracle服务器中
③ PLSQL不能被其它应用程序调用,例如:Java
2、存储过程与PLSQL是什么关系?
存储过程是PLSQL一个方面的应用,PLSQL是存储过程的基础
3、存储过程语法
① 创建无参存储过程hello,无返回值,语法:create or replace procedure 过程名 as PLSQL程序
create or replace procedure hello
as
begin
dbms_output.put_line('这是我的第一个存储过程');
end;
/
② 删除存储过程hello,语法:drop procedure 过程名
drop procedure hello
③ 调用存储过程
◇ 方式一:exec 存储过程名
◇ 方式二:PLSQL程序
begin
hello;
end;
/
◇ 方式三:Java程序
4、创建有参存储过程raiseSalary(编号),为7369号员工涨10%的工资,演示in的用法,默认in,大小写不敏感
create or replace procedure raiseSalary(pempno in number)
as
begin
update emp set sal = sal*1.1 where empno = pempno;
end;
/
5、创建有参存储过程findEmpNameAndSalAndJob(编号),查询7788号员工的的姓名,职位,月薪,返回多个值,演示out的用法
create or replace procedure findEmpNameAndSalAndJob(pempno in number,pname out varchar2,pjob out varchar2,psal out number)
as
begin
select name,job,sal into pname,pjob,psal from emp where empno=pempno;
end;
/
declare
pname emp.name%type;
pjob emp.job%type;
psal emp.sal%type;
begin
findEmpNameAndSalAndJob(7788,pname,pjob,psal);
dbms_output.put_line('7788'||':'|| pname ||'---'|| pjob ||'---'|| psal);
end;
/
6、什么情况下用exec调用,什么情况下用PLSQL调用存储过程?
exec适应于过程没有返回值,PLSQL适应于过程有返回值
7、存储函数
函数(Function)为一命名的存储程序,可带参数,并返回一个计算值。函数和过程的结构类似,但必须有一个RETURN子句,用于返回函数值。函数说明要指定函数名、结果值的类型,以及参数类型等。
8、存储函数语法
① 创建无参存储函数getName,有返回值,语法:create or replace function 函数名 return 返回类型 as PLSQL程序段
create or replace function getName
return varchar2
begin
return "哈哈";
end;
/
② 删除存储函数getName,语法:drop function 函数名
③ 调用存储函数
◇ 方式一:PLSQL程序
declare
name varchar2(10);
begin
name :=getName();
dbms_output.put_line(name);
end;
/
◇ 方式二:Java程序
9、创建有参存储函数findEmpIncome(编号),查询7369号员工的年收入,演示in的用法,默认in
create or replace function findEmpIncome(pempno in number)
return number
as
v_sal number;
begin
select sal*12+NVL(comm,0) into v_sal from emp where empno=pempno;
return v_sal;
end;
/
declare
v_sal number;
begin
v_sal:=queryEmpSalary(7369);
dbms_output.put_line('salary is:' || v_sal);
end;
/
10、创建有参存储函数findEmpNameAndJobAndSal(编号),查询7788号员工的的姓名(return),职位(out),月薪(out),返回多个值
create or replace function findEmpNameAndJobAndSal(pempno in number,pjob out varchar2,psal out number)
return varchar2
as
pname emp.name%type;
begin
select name,job,sal into pname,pjob,psal from emp where empno=pempno;
return pname;
end;
/
declare
pname emp.name%type;
pjob emp.job%type;
psal emp.sal%type;
begin
pname:=findEmpNameAndJobAndSal(7788,pjob,psal);
dbms_output.put_line('7788'||':'|| pname ||'---'|| pjob ||'---'|| psal);
end;
/
11、过程和函数中的in和out
一般来讲,过程和函数的区别在于函数至少有一个返回值,而过程至少没有返回值。但过程和函数都可以通过out指定一个或多个输出参数,我们可以利用out参数,在过程和函数中实现返回多个值。
10、过程和函数适合场景
【推荐原则】如果只有一个返回值,用存储函数;否则,就用存储过程。
12、什么情况【适合使用】过程函数,什么情况【适合使用】SQL?
① 过程函数:
◇ 需要长期保存在数据库中
◇ 需要被多个用户重复调用
◇ 业务逻辑相同,只是参数不一样
◇ 批操作大量数据,例如:批量插入很多数据
② SQL:
◇ 凡不是上述,都可使用SQL
◇ 对表、视图、序列、索引等这些还是要用SQL
十、触发器(Trigger)
1、什么是触发器?
不同的DML(select、Insert、update、delete)操作,触发器能够进行一定的拦截,符合条件操作方可操作基表,反之不可操作基表。
触发器可用于:数据确认(后) 、安全性检查(前)
2、为什么要用触发器?
如果没有触发器,那么DML所有操作均可无限制操作基表,可能不符合业务需求。
3、触发器语法
① 创建触发器
CREATE [or REPLACE] TRIGGER 触发器名
{BEFORE | AFTER}
{ INSERT | DELETE|-----语句级
UPDATE OF 列名}----行级
ON 表名
[FOR EACH ROW]
PLSQL 块【declare…begin…end;/】
② 删除触发器insertEmpTrigger,语法:drop trigger 触发器名
4、创建语句级触发器insertEmpTrigger,当对表【emp】进行增加【insert】操作前【before】,显示"hello world"
create or replace trigger insertEmpTrigger
before
insert
on emp
declare
begin
dbms_output.put_line('hello world');
end;
/
5、创建语句级触发器deleteEmpTrigger,当对表【emp】进行删除【delete】操作后【after】,显示"world hello"
create or replace trigger deleteEmpTrigger
after
delete
on emp
declare
begin
dbms_output.put_line('hello world');
end;
/
6、星期一到星期五且7-23点能向数据库emp表插入数据,否则使用函数抛出异常,语法:raise_application_error('-20000','例外原因')
create or replace trigger securityTrigger
before
insert
on emp
declare
pday varchar2(10);
phour number(2);
begin
select to_char(sysdate,'day') into pday from dual;
select to_char(sysdate,'hh24') into phour from dual;
if pday in ('星期六','星期日') or phour not between 7 and 23 then
raise_application_error('-20000','非工作时间');
else
--插入数据
end if;
end;
/
7、创建行级触发器checkSalaryTrigger,涨后工资这一列,确保大于涨前工资,语法:for each row/:new.sal/:old.sal
create or replace trigger checkSalaryTrigger
after
update of sal
on emp
for each row
declare
pday varchar2(10);
phour number(2);
begin
if :new.sal<=:old.sal then
raise_application_error('-20200','工资涨幅异常');
end if;
end;
/
触发语句 | old | new |
---|---|---|
Insert | 所有字段都是空(null) | 将要插入的数据 |
Update | 更新以前该行的值 | 更新后行的值 |
Delete | 删除以前该行的值 | 所有字段都是空(null) |
8、触发器的类型
① 语句级触发器
在指定的操作语句操作之前或之后执行一次,不管这条语句影响了多少行 。
② 行级触发器(FOR EACH ROW)
触发语句作用的每一条记录都被触发。在行级触发器中使用:old和:new伪记录变量, 识别值的状态。
8、删除触发器,表还在吗?【还在】
将表丢到回收站,触发器还在吗?【还在】
当闪回表后,触发器会在吗?【还在】
彻底删除表,触发器会在吗?【不在了】
十一、oracleSQL优化方案
1、为什么要Oracle优化
随着实际项目的启动,Oracle经过一段时间的运行,最初的Oracle设置,会与实际Oracle运行性能会有一些差异,这时我们就需要做一个优化调整。
2、Oracle优化这个课题较大,可分为四大类
① 主机性能
② 内存使用性能
③ 网络传输性能
④ SQL语句执行性能【程序员】
3、下面列出一些oracleSQL优化方案
① 选择最有效率的表名顺序(笔试常考)
ORACLE的解析器按照从右到左的顺序处理FROM子句中的表名, FROM子句中写在最后的表将被最先处理,在FROM子句中包含多个表的情况下,你必须选择记录条数最少的表放在最后,如果有3个以上的表连接查询,那就需要选择那个被其他表所引用的表放在最后。
例如:查询员工的编号,姓名,工资,工资等级,部门名
select emp.empno,emp.ename,emp.sal,salgrade.grade,dept.dname
from salgrade,dept,emp
where (emp.deptno = dept.deptno) and (emp.sal between salgrade.losal and salgrade.hisal)
◇ 如果三个表是完全无关系的话,将记录和列名最少的表,写在最后,然后依次类推
◇ 如果三个表是有关系的话,将引用最多的表,放在最后,然后依次类推
② WHERE子句中的连接顺序(笔试常考)
ORACLE采用自右而左的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他WHERE条件之左,那些可以过滤掉最大数量记录的条件必须写在WHERE子句的之右。
例如:查询员工的编号,姓名,工资,部门名
select emp.empno,emp.ename,emp.sal,dept.dname
from emp,dept
where (emp.deptno = dept.deptno) and (emp.sal > 1500)
③ SELECT子句中避免使用*
号
ORACLE在解析的过程中,会将*依次转换成所有的列名,这个工作是通过查询数据字典完成的,这意味着将耗费更多的时间
select empno,ename from emp;
④ 使用DECODE函数来减少处理时间
使用DECODE函数可以避免重复扫描相同记录或重复连接相同的表
⑤ 整合简单,无关联的数据库访问
⑥ 用TRUNCATE替代DELETE
⑦ 尽量多使用COMMIT,因为COMMIT会释放回滚点
⑧ 用WHERE子句替换HAVING子句:WHERE先执行,HAVING后执行
⑨ 多使用内部函数提高SQL效率
⑩ 使用表的别名
⑪ 使用列的别名
⑫ 用索引提高查询效率
⑬ 字符串型,能用=号,不用like
因为=号表示精确比较,like表示模糊比较
⑭ SQL语句用大写的
因为Oracle服务器总是先将小写字母转成大写后,才执行。在eclipse中,先写小写字母,再通过ctrl+shift+X转大写;ctrl+shift+Y转小写
⑮ 避免在索引列上使用NOT
因为Oracle服务器遇到NOT后,他就会停止目前的工作,转而执行全表扫描
⑯ 避免在索引列上使用计算
WHERE子句中,如果索引列是函数的一部分,优化器将不使用索引而使用全表扫描,这样会变得变慢
◇ 低效:
SELECT EMPNO,ENAME
FROM EMP
WHERE SAL*12 > 24000;
◇ 高效:
SELECT EMPNO,ENAME
FROM EMP
WHERE SAL > 24000/12;
⑰ 用 >= 替代 >
◇ 低效:
SELECT * FROM EMP WHERE DEPTNO > 3
首先定位到DEPTNO=3的记录并且扫描到第一个DEPT大于3的记录
◇ 高效:
SELECT * FROM EMP WHERE DEPTNO >= 4
直接跳到第一个DEPT等于4的记录
⑱ 用IN替代OR
select * from emp where sal = 1500 or sal = 3000 or sal = 800;
select * from emp where sal in (1500,3000,800);
⑲ 总是使用索引的第一个列
如果索引是建立在多个列上,只有在它的第一个列被WHERE子句引用时,优化器才会选择使用该索引,当只引用索引的第二个列时,不引用索引的第一个列时,优化器使用了全表扫描而忽略了索引
create index emp_sal_job_idex
on emp(sal,job);
select *
from emp
where job != 'SALES';
⑳ 避免改变索引列的类型,显示比隐式更安全
当字符和数值比较时,ORACLE会优先转换数值类型到字符类型
select 123 || '123' from dual;