进阶学习之Oracle增强

版权声明:使用本品须Jack Gao同意,违者必究 https://blog.csdn.net/Mr_GaoYang/article/details/84783640

一、增删改数据

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;

猜你喜欢

转载自blog.csdn.net/Mr_GaoYang/article/details/84783640