08-Oracle高级

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/linzhaoliangyan/article/details/88557374

1 SQL Developer 连接

2 PLSQL Developer连接

    * PLSQL Developer 配置远程链接

        * 参考配置文档

    * 参考链接:https://blog.csdn.net/u013882957/article/details/71308003

    * 使用教程:https://jingyan.baidu.com/article/c85b7a6403acd1003bac950f.html

3 表空间

      * 参考链接:https://baike.baidu.com/item/%E8%A1%A8%E7%A9%BA%E9%97%B4/1269860?fr=aladdin

    3.1 数据库由若干表空间构成,表空间由一到多个数据文件组成,每个数据文件只能属于同一表空间。
    3.2 分类    

* 永久性表空间:一般保存表、视图、过程和索引等的数据

* 临时性表空间:只用于保存系统中短期活动的数据

* 撤销表空间:用来帮助回退未提交的事务数据

 

    3.3 查询表空间

SELECT file_name,tablespace_name,bytes,autoextensible 
  FROM dba_data_files 

3.4 创建表空间

    * 创建表空间示例,C盘需要先创建oracle_data目录,以存放数据文件

create tablespace hxdata1
datafile 'C:\oracle_data\hxdata1.dbf' size 20M
autoextend on;

  * 查询是否创建成功

SELECT file_name,tablespace_name,bytes,autoextensible 
  FROM dba_data_files where tablespace_name='HXDATA1'; 

    3.5  调整hxdata1表空间大小,向表空间内添加数据文件

alter tablespace hxdata1
add datafile 'C:\oracle_data\hxdata2.dbf' size 30M
autoextend on;

3.6  删除hxdata1表空间

       *  只删除表空间

drop tablespace hxdata1;

 *  删除表空间及数据文件

drop tablespace hxdata1 including contents and datafiles;

4 用户与权限

     4.1系统用户
         * sys用户:超级用户,主要用来维护系统信息和管理实例,以SYSDBA或SYSOPER角色登录。
         * system用户: 默认的系统管理员,拥有DBA权限,通常用来管理Oracle数据库的用户、权限和存储,以Normal方式登录。
         * scott用户:示范用户,使用users表空间。

    4.2用户与模式
         * 模式为数据库对象(如表、索引等)的集合,oracle会为每个用户创建一个模式,和用户名一样。

         * 参考链接:https://www.2cto.com/database/201708/674637.html

    4.3创建用户与权限

        *  创建表空间,以便新建用户时使用

create tablespace hxdata3
datafile 'C:\oracle_data\hxdata3.dbf' size 30M
autoextend on next 20M;

    *  创建用户

create user user_hx identified by 123123
default tablespace hxdata3
temporary tablespace temp;

       *  查询user_hx用户

SELECT *
  FROM dba_users 
 WHERE username='USER_HX';

 *   给户user_hx用户授权,回收权限  

    * 参考链接:https://blog.csdn.net/jionjionyoushen/article/details/6803341

    * 没有授权之前登录

 * 授权

GRANT connect, resource TO user_hx;

GRANT SELECT ON SCOTT.emp TO  user_hx; --允许用户查看 EMP 表中的记录(讲解模式)
GRANT UPDATE ON SCOTT.emp TO user_hx; --允许用户更新 EMP 表中的记录
REVOKE connect, resource FROM user_hx; --撤销CONNECT和RESOURCE两个角色 
/
--修改密码
ALTER USER user_hx identified by user10;
-- 删除用户
DROP USER user_hx CASCADE; 
--DROP TABLESPACE hxdata3 INCLUDING CONTENTS AND DATAFILES;--删除表空间及数据文件

5 Oracle数据类型二

    5.1dual表

理解为:是一张只有一行记录的表.不存主题数据,我们也称为“伪表” ,便于select特定对象.

    5.2字符数据类型 

CHAR:存储固定长度的字符串,单字节字符,长度为1-2000.

VARCHAR2:存储可变长度的字符串,单字节字符,长度为1-4000。 

NCHAR和NVARCHAR2:存储Unicode字符集类型(双字节字符),NCHAR长度为1-1000,NVARCHAR2长度为1-2000

    5.3数值数据类型 

* NUMBER:存储整数和浮点数,格式为NUMBER(p)或NUMBER(p, s),P为有效数位(小数点和-号不计),长度为1-38,s为小数点右边的数字位数(则有效的整数位‘‘不为0开始算’’为p-s位)。

    5.4日期时间数据类型 

* DATE:存储日期和时间数据 ,设置格式:环境变量添加:nls_date_format的值为YYYY-MM-DD HH24:MI:SS

* 插入日期示例:insert into 表名  values(列值, ……,to_date( '2014-10-10 12:12:22','YYYY-MM-DD HH24:MI:SS'));可以此基础上去掉(值和格式都要去掉)秒,分,时,日,月等,分秒去掉默认为0,时分秒去掉,就只有年月日,日去掉默认为1号,月去掉默认为系统当前月。

         * TIMESTAMP:秒值精确到小数点后6位,

插入数据示例:insert into tt12  values(1,0.333333333,to_timestamp('2014-10-10 12:12:25.112233','YYYY-MM-DD HH24:MI:SS.FF ')); 

可以此基础上去掉(值和格式都要去掉)秒,分,时,日,月等,分秒去掉默认为0,时分秒去掉,时默认为12点(可能跟设置有关),日去掉默认为1号,月去掉默认为系统当前月。

    5.5 其它数据类型 
        VARCHAR、 INTEGER、FLOAT、DOUBLE、lob(BLOB、CLOB、BFILE、NCLOB) 

6 伪列

     6.1Rowid
         * 数据库中行的地址(唯一),可快速定位行。

 
SELECT ROWID,字段名……
 FROM 表名;
SELECT ROWID,字段名……
  FROM 表名;

   * rowid 可以在查询时通过行ID去查找,尤其是部分重复数据行,用行ID查找就很方便了

  6.2Rownum

*给查询结果返回一个数值表示次序,可以用来限制返回的条数,例如分页查询。

select rownum rn,ename from emp where rownum<8

注意:rownum大于1的其它正整数时,条件不成立

所以查询某页的记录:例如第4到7条记录,就不能写成:

select rownum rn,ename from emp where rownum<8 and rownum>3;

可以写生如下语句:

select * from (select rownum rn ,t.* from tt12 t where rownum<8) where rn>4;

7 数据控制语言

给户user_it用户授权,回收权限  
GRANT connect, resource TO user_it;  --授予CONNECT和RESOURCE两个角色(讲解权限)
REVOKE connect, resource FROM user_it; --撤销CONNECT和RESOURCE两个角色 
GRANT SELECT ON SCOTT.emp TO  user_it; --授予用户user_it查看 用户SCOTT 模式下的EMP 表中的记录权限
REVOKE  SELECT ON SCOTT.emp from user_it; --取消用户user_it查看 用户SCOTT 模式下的EMP 表中的记录权限
GRANT UPDATE ON SCOTT.emp TO user_it; --授予用户user_it更新 EMP 表中的记录权限

8 事务控制语言

    * 参考链接:https://www.cnblogs.com/zf29506564/p/5772380.html

    * 8.1 事务概念

        * 事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功

 例如:A——B转帐,对应于如下两条sql语句

    update from account set money=money+100 where name='B';

    update from account set money=money-100 where name='A'

    * 8.2 事务控制语句,主要由commit\rollback\savepoint savepoint_name\rollback to savepoint_name组成。以下为使用示例:

/*
\\事务控制语句应用举例
*/
--DROP TABLE employee;
--执行步骤一:创建employee表
CREATE TABLE employee 
(    
     emp_no CHAR(8) PRIMARY KEY NOT NULL,   --工号,主键,非空
     emp_name VARCHAR2(30) NOT NULL,--姓名,非空      
     emp_id VARCHAR2(18), --身份证号,代表18位整数
     emp_age NUMBER(3,0)  --年龄
);                         
--执行步骤二:插入数据
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh001','张大','441521199909092111',19);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh002','张二','441521199909092112',19);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh003','张三','441521199909092113',18);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh004','张四','441521199909092114',18);

--执行步骤三:操作employee表
SAVEPOINT a;
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh005','张五','441521199909092115',18);
SAVEPOINT b;
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh006','张6','441521199909092116',18);
--执行步骤四:查看employee表,
SELECT * FROM employee;
ROLLBACK TO SAVEPOINT b;
SELECT * FROM employee;
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh007','张7','441521199909092117',18);
--执行步骤五:查看employee表,
SELECT * FROM employee;
ROLLBACK TO SAVEPOINT a;
SELECT * FROM employee;
--执行步骤六:回滚
ROLLBACK;
--执行步骤七:查看employee表
SELECT * FROM employee;

  * 8.3  事务的四大特性(ACID)

    * A(Atomicity):原子性

        原子性是指事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败

    * C(Consistency):一致性(能量守恒)

        事务必须使数据库从一个一致性状态变换到另外一个一致性状态

    * I(Isolation):隔离性

        事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。

    * D(durability):持久性

        持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响

   * 8.4 I(Isolation):隔离性(并发)(Oracle默认的隔离级别是read committed)

        * 事务隔离级别

            多个线程开启各自事务操作数据库中数据时,数据库系统要负责隔离操作,以保证各个线程在获取数据时的准确性。

    1. Serializable(串行化):可避免脏读、不可重复读、虚读情况的发生。
    2. Repeatable read(可重复读):可避免脏读、不可重复读情况的发生。
    3. Read committed(读已提交):可避免脏读情况发生。
    4. Read uncommitted(读未提交):最低级别,以上情况均无法保证。

         * 假如不考虑隔离,会引发以下问题:

            1 脏读

                脏读指一个事务读取了另外一个事务未提交的数据

            2 不可重复读

                不可重复读指在一个事务内读取表中的某一行数据,多次读取结果不同。

                不可重复读和脏读的区别脏读是读取前一事务未提交的脏数据,不可重复读是重新读取了前一事务已提交的数据

            3 幻读(虚读)

                虚读(幻读)是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致

          

        * Oracle数据库支持READ COMMITTED 和 SERIALIZABLE这两种事务隔离级别。所以Oracle不支持脏读

    * 8.5 隔离级别的演示(mysql的比较好演示隔离级别,默认没有开启事务)

 9 运算符

     9.1算术运算符
        +、-、*、/等略。
     9.2比较(关系)运算符

=、!=、< 、 > 、 <= 、 >= 、 between...and... 、  in(not in) 、 like 、 is null。

  

is null使用注意:
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh008','张8',null,18);--emp_id值为null
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh009','张9','',18);--emp_id值为null
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh008','张8','null',18);--emp_id值不为null
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh009','张9','   ',18);--emp_id值不为null

  9.3逻辑运算符

and  、 or 、 not。

    9.4连接运算符

||

SELECT emp_id||emp_name FROM employee;

    9.5集合运算符

union(并集无重复)

union all(并集有重复)

intersect(交集,共有部分)

minus(减集,第一个查询具有,第二个查询不具有的数据)

使用注意:

列数相关,对应列的数据类型兼容,不能含有Long类型的列,第一个select语句的列或别名作为结果标题

9.5.1准备工作
学校校务人员需要修订员工信息,系统维护人员对员工表进行备份后,校务人员操作了员工信息表,进行了数据处理,因为某种原因,操作失误,现领导需要查询所有员工的信息,可重复, 没有变动的员工,如果有变动,找出哪些被修改过的员工,包括删除、修改、新增的。
/*
\创建员工表
*/
--drop table employee;
--drop table employee_bak
CREATE TABLE employee 
(    
     emp_no CHAR(8) PRIMARY KEY NOT NULL,   --工号,主键,非空
     emp_name VARCHAR2(30) NOT NULL,--姓名,非空      
     emp_id VARCHAR2(18), --身份证号,代表18位整数
     emp_age NUMBER(3,0)  --年龄
);
--插入员工表原有员工数据
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh001','张大','441521199909092111',19);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh002','张二','441521199909092112',19);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh003','张三','441521199909092113',18);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh004','张四','441521199909092114',18);
--维护人员备份数据,利用现有表创建新表(及数据),select 后边有多少字段,新表将有多少个字段
CREATE TABLE employee_bak AS SELECT * FROM employee;

--校务人员处理数据
update employee set emp_id='331521199909092115' where emp_no='gh003';
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh005','张五','441521199909092115',18);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh006','张六','441521199909092116',19);
delete from  employee where emp_no ='gh004';
commit;
select * from employee;
select * from employee_bak;
9.5.2使用示例

--union使用示例,需要查看所有完整的员工的信息
select emp_no,emp_name,emp_id,emp_age from employee union select emp_no,emp_name,emp_id,emp_age from employee_bak order by emp_no;

--使用order by时 select后不能用* 号,如:select * from employee union select * from employee_bak order by emp_no;

--扩展练习:查询所有员工的信息,可重复, 没有变动的员工,如果有变动,找出哪些被修改过的员工,包括删除、修改、新增的


--union all, --这里仍然以员工表为示例,查询所有员工的信息,可重复
select emp_no,emp_name,emp_id,emp_age from employee union all select emp_no,emp_name,emp_id,emp_age from employee_bak order by emp_no;

--使用order by时 select后不能用* 号,如:select * from employee union select * from employee_bak order by emp_no;

--intersect使用示例,找出信息没有变动的员工
select emp_no,emp_name,emp_id,emp_age from employee intersect  select emp_no,emp_name,emp_id,emp_age from employee_bak order by emp_no;

--mimus使用示例,找出备份后被删除或修改的员工信息
select emp_no,emp_name,emp_id,emp_age from employee_bak minus  select emp_no,emp_name,emp_id,emp_age from employee order by emp_no;

--mimus使用示例,找出新增的员工信息
select * from employee where emp_no in(
select emp_no from employee minus  select emp_no from employee_bak
);    

0 函数

    10.1多行函数

sum:求和

avg:求平均数

count:计数

max:求最大值

min:求最小值

分组与过滤

group by与having使用

    10.2转换函数

10.2.1 TO_CHAR

一般用来格式化日期数据或数值
SELECT TO_CHAR(sysdate,'YYYY-MM-DD HH24:MI:SS') FROM dual;
SELECT to_char(1210.7, '$9,999.00') FROM dual;

  10.2.2 TO_DATE

将字符串转为日期类型。

示例:insert into 表名  values(列值, ……,to_date( '2014-10-10 12:12:22','YYYY-MM-DD HH24:MI:SS'));可以此基础上去掉(值和格式都要去掉)秒,分,时,日,月等,分秒去掉默认为0,时分秒去掉,就只有年月日,日去掉默认为1号,月去掉默认为系统当前月。 

10.2.3 TO_NUMBER
        将字符串转为数字(通常能隐式转换)

SELECT to_number('16')+3 FROM dual;
结果等同于下边
SELECT '16'+3 FROM dual;

 10.3分析函数
        10.3.1准备工作

/*
\创建员工表
*/
--drop table employee;
CREATE TABLE employee
(    
     emp_no CHAR(4) PRIMARY KEY NOT NULL,   --工号,主键,非空
     emp_name VARCHAR2(30) NOT NULL,--姓名,非空      
     emp_id VARCHAR2(18), --身份证号,代表18位整数
     emp_age NUMBER(3,0)  --年龄
);

insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh001','张大','441521199909092111',19);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh002','张二','441521199909092112',19);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh003','张三','441521199909092113',18);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh004','张四','441521199909092114',18);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh005','张三','441521199909092113',18);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh006','张四','441521199909092114',18);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh007','张7','441521199909092114',18);
commit;

10.3.2使用示例

分析函数可以每个组返回多行。RANK,DENSE_RANK,ROW_NUMBER三个函数的使用示例: 

--查询各个年龄的情况,PARTITION BY类似group by,根据ORDER BY排序字段的值重新由一开始排序。
--RANK使用相同排序排名一样,后继数据空出排名
SELECT t.*,
 RANK( ) OVER (PARTITION BY emp_age ORDER BY emp_ID DESC) as myRANK
 from employee t;
--DENSE_RANK使用,使用相同排序排名一样,后继数据不空出排名
SELECT t.*,
 DENSE_RANK( ) OVER (PARTITION BY emp_age ORDER BY emp_ID DESC) as myDENSERANK
 from employee t;
--ROW_NUMBER使用,不管排名是否一样,都按顺序排名
SELECT t.*,
 ROW_NUMBER( ) OVER (PARTITION BY emp_age ORDER BY emp_ID DESC) as myDENSERANK
 from employee t;

* 参考链接:https://blog.csdn.net/u011944141/article/details/78927715

10.4其它函数

* NVL(EXP1,EXP2): EXP1的值不为null,返回自己,否则返回EXP2;

* NVL2(EXP1,EXP2,EXP3): EXP1的值不为null,将返回EXP2,否则返回EXP3;

* DECODE(VALUE,IF1,THEN1,IF2,THEN2,...,ELSE):如果value等于if1,则返回then1,如果value等于if2,则返回then2,...否则返回else的值.

--创建员工表,插入测试数据
--drop table employee;
CREATE TABLE employee
(    
     emp_no CHAR(8) PRIMARY KEY NOT NULL,   --工号,主键,非空
     emp_name VARCHAR2(30) NOT NULL,--姓名,非空      
     emp_id VARCHAR2(18), --身份证号,代表18位整数
     emp_address varchar2(30)  
);
insert into employee(emp_no,emp_name,emp_id,emp_address) values('gh001','张大','441521199909092111','4401');
insert into employee(emp_no,emp_name,emp_id,emp_address) values('gh002','张二','441521199909092112','4403');
insert into employee(emp_no,emp_name,emp_id,emp_address) values('gh003','张三','441521199909092113','4401');
insert into employee(emp_no,emp_name,emp_id) values('gh004','张四','441521199909092114');
commit;

select emp_no as 工号,nvl(emp_address,'4401') as 住址 from employee;
select emp_no 工号,nvl2(emp_address,'4401','0000') 住址 from employee;
select emp_no 工号,decode(emp_address,4401,'广州','4403','深圳','其它') 住址  from employee;

 11 连接查询

连接查询
CREATE TABLE employee
(    
     emp_no CHAR(8) PRIMARY KEY NOT NULL,   --工号,主键,非空
     emp_name VARCHAR2(30) NOT NULL,--姓名,非空      
     emp_id VARCHAR2(18), --身份证号,代表18位整数
     emp_address varchar2(30),
     tea_no  CHAR(4)
);

create table teacher(
  tea_no CHAR(8) PRIMARY KEY NOT NULL,   --工号,主键,非空
  tea_name VARCHAR2(30) NOT NULL--姓名,非空      
);
insert into teacher  values('t001','郑大');
insert into teacher  values('t002','郑二');
insert into teacher  values('t003','郑三');
commit;



insert into employee(emp_no,emp_name,emp_id,emp_address,tea_no) values('gh001','张大','441521199909092111','4401','t001');
insert into employee(emp_no,emp_name,emp_id,emp_address,tea_no) values('gh002','张二','441521199909092112','4403','t002');
insert into employee(emp_no,emp_name,emp_id,emp_address,tea_no) values('gh003','张三','441521199909092113','4401','t001');
insert into employee(emp_no,emp_name,emp_id) values('gh004','张四','441521199909092114');
insert into employee(emp_no,emp_name,emp_id) values('gh005','张5','441521199909092115');
commit;
select * from employee;
select * from teacher;

select * from employee s inner join teacher t on s.tea_no = t.tea_no;--inner join:两个表关联后都有数据的记录显示
select * from employee s left join teacher t on s.tea_no = t.tea_no;--left join:左边的全数据都显示,右边没在关联的不显示
select * from employee s right join teacher t on s.tea_no = t.tea_no;--right jion:右边的全数据都显示,左边没在关联的不显示
select * from employee s full join teacher t on s.tea_no = t.tea_no;--full join:两边都会显示

12 索引

    * 参考链接:https://www.cnblogs.com/wishyouhappy/p/3681771.html

    * 与表关联,可提供快速访问数据方式
    * 常用类型(按逻辑分类):单列索引和组合索引、唯一索引和非唯一索引(索引的列值是否唯一)
    11.1创建索引

CREATE [UNIQUE] INDEX  index_name ON table_name(column_list);
示例:
 --创建单列唯一索引
CREATE unique INDEX  index_emp_emp_id ON employee(emp_id);
--创建单列非唯一索引
CREATE  INDEX  index_emp_emp_id ON employee(emp_id);
--创建组合列、唯一索引
CREATE unique INDEX  index_emp_emp_name_emp_id ON employee(emp_name,emp_age);
--创建组合列、非唯一索引
CREATE INDEX  index_emp_emp_name_emp_id ON employee(emp_name,emp_age);

11.2删除索引

语法:DROP INDEX  index_name;
--删除索引
drop index index_emp_emp_name_emp_id;

 注意:删除表时会把表的索引删除掉

13 序列

    * 参考链接:https://www.cnblogs.com/always-online/p/4029703.html

    13.1创建序列

语法:
CREATE SEQUENCE sequence_name
[START WITH integer]
[INCREMENT BY integer]
[MAXVALUE integer| NOMAXVALUE]
[MINVALUE integer| NOMINVALUE]
[CYCLE| NOCYCLE]
[CACHE integer|NOCACHE];
[order] --单实例不用设置,rac时候使用,保证不同实例上产生的序列号被取用的时候按顺序进行

CREATE SEQUENCE seq_test
START WITH 1
INCREMENT BY 1
MAXVALUE 2000
NOCYCLE
CACHE 30;

 13.2使用序列

* NEXTVAL:第一次访问时,返回序列的初始值,后继每次调用时,按步长增加的值返回。

* CURRVAL:返回序列当前的值,创建新序列后,不能直接使用CURRVAL访问序列,使用过NEXTVAL访问序列后才能使用。

select seq_test.nextval from dual;
select seq_test.currval from dual;
使用示例
/*
\创建员工表
*/
--drop table employee;
--drop table employee_bak
CREATE TABLE employee
(    
     emp_no number(8) PRIMARY KEY NOT NULL,   --工号,主键,非空
     emp_name VARCHAR2(30) NOT NULL,--姓名,非空      
     emp_id VARCHAR2(18), --身份证号,代表18位整数
     emp_age NUMBER(3,0)  --年龄
);
--创建序列
CREATE SEQUENCE seq_emp_no
START WITH 1
INCREMENT BY 1
MAXVALUE 2000
MINVALUE 1
NOCYCLE
CACHE 30;


--使用序列插入数据
insert into employee(emp_no,emp_name,emp_id,emp_age) values(seq_emp_no.nextval,'张大','441521199909092111',18);


--查询数据,如果发现表中的数据从2开始,可以修改deferred segment creation(创建延迟片断)的参数
--执行alter session set deferred_segment_creation=false; 把参数的值高为false,此参数为11gR2版本新参数
SELECT * FROM employee;
SELECT seq_emp_no.CURRVAL FROM dual;

--修改序列
ALTER SEQUENCE seq_emp_no
MAXVALUE 5000
CYCLE;

13.3删除序列

--删除序列
DROP SEQUENCE seq_emp_no;

 13.4序列与SYS_GUID函数(生成唯一标识符)

--使用SYS_GUID函数,32位,由时间戳和机器标识符生成,保证在不同数据库是唯一
SELECT sys_guid() FROM dual;

4 同义词(了解)

     * 参考链接:https://www.cnblogs.com/oraclestudy/articles/5779180.html

     * 提供数据库对象的别名,简化SQL语句,隐藏对象名称和所有者,为分布式数据库远程对象提供位置透明性。

    14.1私有同义词

只能被当前模式下用户访问,不能与其它对象同名,需要具有CREATE SYNONYM权限.
使用示例
--system当前用户是system)授权用户user_it创建同义词的权限
grant create synonym to user_hx;
--创建私有同义词
create synonym syn_employee for employee;
create synonym syn_emp for scott.emp;--需要先获取访问此模式下表的相应权限
create synonym syn_seq_emp_no for seq_emp_no;

--使用私有同义词
select syn_seq_emp_no.nextval from dual; 
update syn_emp set ename='ppppp' where empno='7369';

--删除同义词
drop synonym syn_employee

   * 注意授权
    14.2公有同义词

公有同义词一般由管理员用户创建及删除,普通用户需要创建及删除需要create public synonym和drop public synonym权限。下面是普通用户创建和删除同义词为例:

--登陆system用户,授权用户user_it创建、删除公有同义词权限
grant  create public synonym,drop public synonym to user_it;
--revoke create public synonym,drop public synonym from user_it;

--登陆user_it用户创建公有同义词
create public synonym syn_public_employee for employee;

--登陆user_it用户使用公有同义词
select * from syn_public_employee; 
--为其它用户user_it1授权使用公有同义词(需要给予使用表的权限)
grant select,update on employee to user_it1;
--revoke select,update on employee from user_it1;

--登陆user_it1用户下使用公有同义词syn_public_employee
select * from syn_public_employee; 
update syn_public_employee set  emp_name='aaa' where emp_no=2; 

--删除同义词
--登陆user_it,删除公有同义词
drop public synonym syn_public_employee;

15 分区表

         * 参考链接:https://www.cnblogs.com/yumiko/p/6095036.html

* oracle把表中的行分为不同部分,存储在不同的位置,每一部分称为一个分区,分区的表称为分区表。

15.1作用

安全:分区存放于不同的磁盘,减少同时损坏

查询:查询可按分区

管理:可按分区加载、删除

备份和恢复:针对分区备份与恢复,方便

*使用:

数据量大的表,一般大于2GB;数据有明显的界限划分;对于Long和Long Raw类型列不能使用分区。

    15.2分区示例
        15.2.1范围分区

语法:
在Create Table语句后增加
        PARTITION BY RANGE(column_name)
        (
           PARTITION part1 VALUE LESS THAN (range1) [TABLESPACE tbs1],
           PARTITION part2 VALUE LESS THAN (range2) [TABLESPACE tbs2],
           ....
           PARTITION partN VALUE LESS THAN (MAXVALUE) [TABLESPACE tbsN]
        );
示例:
/*
\创建员工表
*/
--drop table employee;
CREATE TABLE employee
(    
     emp_no char(8) PRIMARY KEY NOT NULL,   --工号,主键,非空
     emp_name VARCHAR2(30) NOT NULL,--姓名,非空      
     emp_id VARCHAR2(18), --身份证号,代表18位整数
     emp_record_no number(15), --记录号,表示该生是学样的第几位入学的员工
     emp_birthday date  --出生日期
)
PARTITION BY RANGE (emp_birthday)
(
   PARTITION P1 VALUES LESS THAN (to_date('2011-07-1', 'yyyy-mm-dd')),
   PARTITION P2 VALUES LESS THAN (to_date('2011-10-1', 'yyyy-mm-dd')),
   PARTITION P3 VALUES LESS THAN (maxvalue)
);  
/*
--按入读学校顺序分区
PARTITION BY RANGE (emp_record_no)
(
   PARTITION P1 VALUES LESS THAN (3),
   PARTITION P2 VALUES LESS THAN (6),
   PARTITION P3 VALUES LESS THAN (maxvalue)
);
*/
insert into employee(emp_no,emp_name,emp_id,emp_record_no,emp_birthday) values('gh001','张大','441521199909092111',1,to_date('2011-5-5','yyyy-mm-dd'));
insert into employee(emp_no,emp_name,emp_id,emp_record_no,emp_birthday) values('gh002','张二','441521199909092112',2,to_date('2011-6-5','yyyy-mm-dd'));
insert into employee(emp_no,emp_name,emp_id,emp_record_no,emp_birthday) values('gh003','张三','441521199909092113',3,to_date('2011-7-5','yyyy-mm-dd'));
insert into employee(emp_no,emp_name,emp_id,emp_record_no,emp_birthday) values('gh004','张四','441521199909092114',4,to_date('2011-8-5','yyyy-mm-dd'));
insert into employee(emp_no,emp_name,emp_id,emp_record_no,emp_birthday) values('gh005','张五','441521199909092115',5,to_date('2011-9-5','yyyy-mm-dd'));
insert into employee(emp_no,emp_name,emp_id,emp_record_no,emp_birthday) values('gh006','张六','441521199909092116',6,to_date('2011-10-5','yyyy-mm-dd'));
insert into employee(emp_no,emp_name,emp_id,emp_record_no,emp_birthday) values('gh007','张七','441521199909092117',7,to_date('2011-11-5','yyyy-mm-dd'));
commit;


--要查看在第三季度的数据
SELECT  *  FROM employee partition(P3);

--要删除第三季度的数据
DELETE FROM employee partition(P3);

--查看所有
SELECT  *  FROM employee;

15.2.2列表分区

语法:
        PARTITION BY LIST(column_name)
        (
           PARTITION part1 VALUES (values_list1),
           PARTITION part2 VALUES (values_list2),
           ....
           PARTITION partN VALUES (DEFAULT)
        );
        其中:column_name是以其为基础创建列表分区的列。
              part1...partN是分区的名称。
              values_list是对应分区的分区键值的列表。
              DEFAULT关键字允许存储前面的分区不能存储的记录。

示例
/*
 创建员工表
*/
--drop table employee;
CREATE TABLE employee
(    
     emp_no char(8) PRIMARY KEY NOT NULL,   --工号,主键,非空
     emp_name VARCHAR2(30) NOT NULL,--姓名,非空      
     emp_id VARCHAR2(18), --身份证号,代表18位整数
     emp_record_no number(15), --记录号,表示该生是学校的第几位入学的员工
     emp_province VARCHAR2(50) NOT NULL --所属省份
)
--可以不用写表空间
partition by list (emp_province)
(
  partition bj values ('北京'),
  partition gx_hn values ('广西','海南'),
  partition gd values ('广东'),
  partition qt values (default)
);
/*
partition by list (emp_province)
(
  partition bj values ('北京') tablespace ts_it,
  partition gx_hn values ('广西','海南') tablespace ts_it,
  partition gd values ('广东') tablespace ts_it,
  partition qt values (default) tablespace ts_it
);
*/
           
insert into employee(emp_no,emp_name,emp_id,emp_record_no,emp_province) values('gh001','张大','441521199909092111',1,'广东');
insert into employee(emp_no,emp_name,emp_id,emp_record_no,emp_province) values('gh002','张二','441521199909092112',2,'广西');
insert into employee(emp_no,emp_name,emp_id,emp_record_no,emp_province) values('gh003','张三','441521199909092113',3,'广东');
insert into employee(emp_no,emp_name,emp_id,emp_record_no,emp_province) values('gh004','张四','441521199909092114',4,'北京');
insert into employee(emp_no,emp_name,emp_id,emp_record_no,emp_province) values('gh005','张五','441521199909092115',5,'上海');
insert into employee(emp_no,emp_name,emp_id,emp_record_no,emp_province) values('gh006','张六','441521199909092116',6,'海南');
insert into employee(emp_no,emp_name,emp_id,emp_record_no,emp_province) values('gh007','张七','441521199909092117',7,'广东');
commit;

--要查看在第三分区的数据
SELECT  *  FROM employee partition(qt);

--要删除第三分区的数据
DELETE FROM employee partition(bj);

--查看所有
SELECT  *  FROM employee;

16 PL/SQL

    * 参考链接:https://blog.csdn.net/carrybest/article/details/54949411

        * PL/SQL好处:对于客户/服务器环境来说,真正的瓶颈是网络上。无论网络多快,只要客户端与服务器进行大量的数据交换。应用运行的效率自然就回受到影响。如果使用PL/SQL进行编程,将这种具有大量数据处理的应用放在服务器端来执行。自然就省去了数据在网上的传输时间。

    * PL/SQL:块结构语言,是sql(Structured Query Language)语言的一种扩展,结合了oracle过程语言(procedural language)进行使用。

pl/sql块由三部分构成:声明部分、执行部分、异常部分。
[DECLARE]
    --声明变量等;
BEGIN
    --程序主要部分,一般用来执行过程语句或SQL语句;
[EXCEPTION]
    --异常处理;
END;

16.1运算符

常用运算符:
=    等于     
 比较运算符
<>,!=,~=,^=    不等于    
<    小于    
>    大于    
<=    小于或等于    
>=    大于或等于    
 +    加号     算术运算符
 -    减号    
 *    乘号    
 /    除号    
:=    赋值号    赋值运算符
 =>    关系号    关系号
..    范围运算符    范围运算符
||    字符连接符    连接运算符
is null    是空值     逻辑运算符
between and    介于两者之间    
in     在一系列值中间    
and    逻辑与    
or    逻辑或    
not    取反    

   16.2变量与常量
    * 声明语法

变量:variable_name data_type[(size)][:=init_value];
常量:variable_name CONSTANT  data_type[(size)] :=init_value;

    * 数据类型:

常用标准类型:CHAR(CHARATER,NCHAR),VARCHAR2,NUMBER(P,S),DATE,BOOLEAN等
属性类型:%TYPE与%ROWTYPE
%TYPE:可以用来定义数据变量的类型与已定义的数据变量(表中的列)一致。
%ROWTYPE:与某一数据库表的结构一致(修改数据库表结构,可以实时保持一致);访问方式声明为rowtype的变量名.字段名.

16.2.1 常量或变量基本使用

/*
 准备工作:创建员工表,插入测试数据
*/
--drop table employee;
CREATE TABLE employee 
(    
     emp_no CHAR(8) PRIMARY KEY NOT NULL,   --工号,主键,非空
     emp_name VARCHAR2(30) NOT NULL,--姓名,非空      
     emp_id VARCHAR2(18), --身份证号,代表18位整数
     emp_age NUMBER(3,0)  --年龄
);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh001','张大','441521199909092111',19);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh002','张二','441521199909092112',19);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh003','张三','441521199909092113',18);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh004','张四','441521199909092114',18);
commit;
/*
定义常量或变量、赋值使用示例:定义常量s_class直接存放员工班级,定义变量s_name,s_age分别用来存放工号为gh001的员工(通过查询表)的姓名及年龄,定义变量s_mark存放员工成绩,设定员工成绩为95.5,最后输出该生的班级、姓名、年龄及成绩。
*/
DECLARE
     s_class CONSTANT VARCHAR2(20):='高一(1)班';
     s_name VARCHAR2(30);
     s_age NUMBER(3,0);
     s_mark  NUMBER(3,1);
BEGIN
     --赋值方式一:使用SELECT INTO给变量赋值
     SELECT emp_name, emp_age  INTO   s_name, s_age
      FROM   employee 
     WHERE emp_no='gh001';
    --赋值方式二:使用赋值操作符“:=”给变量赋值
    s_mark:=95.5; 
    --输出相关信息,DBMS_OUTPUT.PUT_LINE为具有输出功能的函数
       DBMS_OUTPUT.PUT_LINE
     ('班级:'||s_class||',姓名:'||s_name||',年龄:'||s_age||',分数:'||s_mark);
END;

--%TYPE的使用示例:在上一示例基础上
DECLARE
     s_class CONSTANT VARCHAR2(20):='高一(1)班';
     s_name employee.emp_name%type;
     s_age employee.emp_age%type;
     s_mark  NUMBER(3,1);
BEGIN
     --赋值方式一:使用SELECT INTO给变量赋值
     SELECT emp_name, emp_age  INTO   s_name, s_age
      FROM   employee
      WHERE emp_no='gh001';
    --赋值方式二:使用赋值操作符“:=”给变量赋值
    s_mark:=95.5; 
    --输出相关信息,DBMS_OUTPUT.PUT_LINE具有输出功能
       DBMS_OUTPUT.PUT_LINE
     ('班级:'||s_class||',姓名:'||s_name||',年龄:'||s_age||',分数:'||s_mark);
END;

--%ROWTYPE的使用
DECLARE
     s_class CONSTANT VARCHAR2(20):='高一(1)班';
     emp_info employee%rowtype;
     s_mark  NUMBER(3,1);
BEGIN
     --赋值方式一:使用SELECT INTO给变量赋值,列数及列顺序要一样,查询列也可以用*号
     SELECT * into emp_info 
      FROM   employee 
     WHERE emp_no='gh001';
    --赋值方式二:使用赋值操作符“:=”给变量赋值
    s_mark:=95.5; 
    --输出相关信息,DBMS_OUTPUT.PUT_LINE为具有输出功能的函数
       DBMS_OUTPUT.PUT_LINE
     ('班级:'||s_class||',姓名:'||emp_info.emp_name||',年龄:'||emp_info.emp_age||',分数:'||s_mark);
END;

16.3控制语句
        16.3.1条件控制

语法1:
if <boolean表达式> then
    执行语句;
end if;
语法2:
if <boolean表达式 >then
    执行语句;
else
    执行语句;
end if;
语法3:
if <boolean表达式> then
    执行语句;
elsif <boolean表达式> then
    执行语句;
elsif <boolean表达式> then
    执行语句;
else
    执行语句;
end if;
使用示例
1。判断工号为gh001的员工是否可以上小学(国家规定需要6周岁才可以上小学),如果可以输出相关信息
2。判断工号为gh001的员工是否可以上小学(国家规定需要6周岁才可以上小学),不论可不可以都输出相关信息
3。判断工号为gh001员工是否适全上小学(国家规定需要6周岁才可以上小学),超过6岁瘵超龄,小于六周岁不能上小学,并根据员工情况输出相关结果。 

/*
 准备工作:创建员工表,插入测试数据
*/
--drop table employee;
CREATE TABLE employee 
(    
     emp_no CHAR(8) PRIMARY KEY NOT NULL,   --工号,主键,非空
     emp_name VARCHAR2(30) NOT NULL,--姓名,非空      
     emp_id VARCHAR2(18), --身份证号,代表18位整数
     emp_age NUMBER(3,0)  --年龄
);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh001','张大','441521199909092111',5);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh002','张二','441521199909092112',6);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh003','张三','441521199909092113',7);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh004','张四','441521199909092114',4);
commit;

/*
示例练习:
1。判断工号为gh001的员工是否可以上小学(国家规定需要6周岁才可以上小学),如果可以输出相关信息
2。判断工号为gh001的员工是否可以上小学(国家规定需要6周岁才可以上小学),不论可不可以都输出相关信息
3。判断工号为gh001员工是否适全上小学(国家规定需要6周岁才可以上小学),超过6岁瘵超龄,小于六周岁不能上小学,
根据员工情况输入相关结果。
*/
--练习1
declare
s_age employee.emp_age%type;
begin
 select emp_age into s_age from employee where emp_no='gh001';
 if s_age>=6 then
             dbms_output.put_line('此员工年龄为:'||s_age||'可以上小学了!');
 end if;  
end;

--练习2
declare
s_age employee.emp_age%type;
begin
 select emp_age into s_age from employee where emp_no='gh001';
 if  s_age>=6 then
             dbms_output.put_line('此员工年龄为:'||s_age||',可以上小学了!');
 else
             dbms_output.put_line('此员工年龄为:'||s_age||',不可以上小学了!');

 end if;  
end;

--练习3
declare
s_age employee.emp_age%type;
begin
 select emp_age into s_age from employee where emp_no='gh001';
 if s_age>6 then
             dbms_output.put_line('此员工年龄为:'||s_age||',超龄了,赶紧上小学了!');
 elsif s_age=6 then
             dbms_output.put_line('此员工年龄为:'||s_age||',刚好可以上小学了!');
 else
             dbms_output.put_line('此员工年龄为:'||s_age||',尚不能上小学!');
 end if;  
end;

   16.3.2循环控制

loop循环
语法:
loop
    执行语句;
    exit when <条件语句>
end loop;
示例:
由于某次考试难度太大,需要给整个班级的员工加分,每次加5分,但最高分不能超过90分

/*
 准备工作:创建员工表,插入测试数据
*/
--drop table employee;
CREATE TABLE employee 
(    
     emp_no CHAR(8) PRIMARY KEY NOT NULL,   --工号,主键,非空
     emp_name VARCHAR2(30) NOT NULL,--姓名,非空      
     emp_id VARCHAR2(18), --身份证号,代表18位字符
     emp_mark NUMBER(3,1)  --分数
);
insert into employee(emp_no,emp_name,emp_id,emp_mark) values('gh001','张大','441521199909092111',50);
insert into employee(emp_no,emp_name,emp_id,emp_mark) values('gh002','张二','441521199909092112',60);
insert into employee(emp_no,emp_name,emp_id,emp_mark) values('gh003','张三','441521199909092113',30);
insert into employee(emp_no,emp_name,emp_id,emp_mark) values('gh004','张四','441521199909092114',40);
commit;
select * from employee;

/*
由于某次考试难度太大,需要给整个班级的员工加分,每次加5分,但最高分不能超过90分
*/
declare
s_mark number(3);
begin
  loop
    select max(emp_mark) into s_mark from employee;      
  exit when (s_mark+5)>90;
   update employee set emp_mark=emp_mark+5;   
  end loop;
  commit;
 end;


while循环
语法:
while <布尔表达式> loop
    执行语句;
end loop;

使用示例
继续使用上面的表,及完成:“由于某次考试难度太大,需要给整个班级的员工加分,每次加5分,但最高分不能超过90分”功能
declare
s_mark number(3):=0;
begin
  while s_mark+5<90 loop
     update employee set emp_mark=emp_mark+5;   
     select max(emp_mark) into s_mark from employee;      
  end loop;
  commit;
 end;

for循环
语法:
for 循环计数器 in [REVERSE] 下限..上限 loop
    执行语句;
end loop;
说明:..两点表示范围,1..4表示时将从1到4进行循环,小(例如 1)写前边,REVERSE表示反转,循环时变成从4到1进行。

使用示例
DECLARE
    flag number(2,0);
BEGIN      
  for flag in  1..4 loop
      dbms_output.put_line('flag ='||flag);
end loop;
END;


继续使用上面的表,及完成:“由于某次考试难度太大,需要给整个班级的员工加分,每次加5分,但最高分不能超过90分”功能。
declare
s_mark number(3);
s_time number;
begin
       select max(emp_mark) into s_mark from employee;
       -- floor 向下取整数的函数(不会四舍五入);         
       s_time:=floor((90-s_mark)/5);       
       --dbms_output.put_line('s_time ='||floor(s_time));         
       for flag in 1..s_time loop
           update employee set emp_mark=emp_mark+5;          
       end loop;      
       commit;
 end;

    16.3.3顺序控制

用于按指定顺序执行的语句。主要包括 null语句和goto语句,goto语句建议不要使用。
null语句:是一个可执行语句,相当于一个占位符或不执行操作的空语句。主要用来提高程序语句的完整性和程序的可读性。

--广东或香港等有些地方不喜欢4的数字,在使用数字时跳过4
DECLARE
flag number(3,0);
BEGIN     
   flag:=0;     
   while flag<10 loop
      flag:=flag+1;      
      if flag=4 then
        --去掉null会的报错
        null;
      else
       dbms_output.put_line('flag ='||flag);

      end if;     
   end loop;
END;


goto语句
示例:广东或香港等有些地方不喜欢4的数字,请使用“goto 标识号”语句在打印数字时跳过4。
--广东或香港等有些地方不喜欢4的数字,在使用数字时跳过4
DECLARE
flag number(3,0);
BEGIN     
   flag:=0;
   --<<>>号,使用goto可以跑到的标记,括号内标记名top不是固定关键字,也可以给其它名称
   <<top>>     
   while flag<10 loop
      flag:=flag+1;      
      if flag=4 then
        --这样可以相当于使用java的continue关键字,跳到上边定义的top开始执行
        goto top;
      end if;     
      dbms_output.put_line('flag ='||flag);
   end loop;
END;

14.4.异常处理
        14.4.1异常语法

Exception
       when 异常名 then
        执行处理语句;
         --使用others确保不会漏过任何异常
       when others then
        执行处理语句;

经常配套使用的函数:
    SQLCODE函数:返回错误代码,
    SQLERRM函数:返回错误信息

例如输出异常信息: DBMS_OUTPUT.PUT_LINE('其它异常,代码号:'||SQLCODE||',异常描述:'||SQLERRM);
16.4.1预定义异常

预定义异常指PL/SQL 程序违反 Oracle 规则或超越系统限制时隐式引发(由oracle自动引发)。
常见的预定义异常:
ZERO_DIVIDE:以零作为除数时出现
DUP_VAL_ON_INDEX:试图将重复的值存储在使用唯一索引的数据库列中
INVALID_NUMBER:试图将一个非有效的字符串转换成数字 
TOO_MANY_ROWS :在执行SELECT INTO语句后返回多行时出现
VALUE_ERROR:变量的值超出变量大小
CURSOR_ALREADY_OPEN:试图打开已经打开的游标
ACCESS_INTO_NULL:试图给一个没有初始化的对象赋值
LOGIN_DENIED :使用无效的用户名和口令登录Oracle
NO_DATA_FOUND :语句无法返回请求的数据



使用示例
--预定义异常
DECLARE    
     s_result NUMBER(3,1);    
BEGIN
    s_result:=10/0;
    --输出相关信息,DBMS_OUTPUT.PUT_LINE为具有输出功能的函数
       DBMS_OUTPUT.PUT_LINE
     ('没有异常!');
     Exception
       when ZERO_DIVIDE then
         DBMS_OUTPUT.PUT_LINE('除数不能为0,请核查!');
         --使用others确保不会漏过任何异常
       when others then
         --SQLCODE函数返回错误代码,SQLERRM函数返回错误信息
         DBMS_OUTPUT.PUT_LINE('其它异常,代码号:'||SQLCODE||',异常描述:'||SQLERRM);
END;
16.4.2自定义异常
自定义异常:程序在运行过程中,编程人员根据业务等情况,认为非正常情况,可以自定义异常。对于这种异常,主要分三步来处理:
第一、定义相关异常;在声明部分定义相关异常,格式:<自定义异常名称> EXCEPTION;
第二、抛出异常;在出现异常部分抛出异常,格式:RAISE <异常名称>;
第三、处理异常;在异常处理部分对异常进行处理,格式:when <自定义异常名称> then ...,      处理异常也可以使用RAISE_APPLICATION_ERROR(ERROR_NUMBER,ERROR_MESSAGE)存 储过程进行处理,其中参数ERROR_NUMBER取值为-20999~-20000的负整数,参数ERROR_MESSAGE为异常文本消息。

使用示例,完成以下功能:
根据业务的需要,提取员工的信息时,需要校验年龄不能为负数,如果为负数,请自定义异常age_exception,并将其抛出,处理。例如以如下的测试数据,取出身份证号:'441521199909092113'的员工,判断其年龄,并进行异常处理。
/*
 创建员工表,插入测试数据
*/
--drop table employee;
CREATE TABLE employee 
(    
     emp_no CHAR(8) PRIMARY KEY NOT NULL,   --工号,主键,非空
     emp_name VARCHAR2(30) NOT NULL,--姓名,非空      
     emp_id VARCHAR2(18), --身份证号,代表18位整数
     emp_age NUMBER(3,0)  --年龄
);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh001','张大','441521199909092111',19);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh002','张二','441521199909092111',19);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh003','张三','441521199909092113',-1);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh004','张四','441521199909092114',18);
commit;

DECLARE
     s_class CONSTANT VARCHAR2(20):='高一(1)班';
     s_name employee.emp_name%type;
     s_age employee.emp_age%type;
     s_mark  NUMBER(3,1);
     age_exception EXCEPTION; --定义异常
BEGIN
     --赋值方式一:使用SELECT INTO给变量赋值
     SELECT emp_name, emp_age  INTO   s_name, s_age
      FROM   employee
      WHERE emp_id='441521199909092111';
      --年龄小于0岁为异常情况,使用raise抛出异常
      if  s_age<0    then
        raise age_exception;
      end if;
      
    --赋值方式二:使用赋值操作符“:=”给变量赋值
    s_mark:=95.5; 
    --输出相关信息,DBMS_OUTPUT.PUT_LINE为具有输出功能的函数
       DBMS_OUTPUT.PUT_LINE
     ('班级:'||s_class||',姓名:'||s_name||',年龄:'||s_age||',分数:'||s_mark);
EXCEPTION 
  when TOO_MANY_ROWS then
       DBMS_OUTPUT.PUT_LINE('异常描述'||Sqlerrm||',原因:身份证号码出现重复');
  WHEN age_exception THEN
       RAISE_APPLICATION_ERROR(-20001,'年龄不能为负数!');
       --DBMS_OUTPUT.PUT_LINE('异常描述:年龄不能为负数!');
  WHEN OTHERS THEN
       DBMS_OUTPUT.PUT_LINE('其它异常,代码号:'||SQLCODE||',异常描述:'||SQLERRM);
END;

17 游标

    * 参考链接:https://www.cnblogs.com/zf29506564/p/5772344.html

    17.1游标概念

        游标是指oracle在执行增删改查操作时,会把执行结果放在内存分配的缓冲区中,游标就是指向该区的一个指针,可以对结果集的每一行数据分别进行处理。
游标属性:
   %found   是否存在结果集或影响的行数,如果存在返回true
     %notfound  是否存在结果集或影响的行数,如果不存在返回true
   %rowcount  返回受影响的行数
   %isopen  游标是否已经打开。隐式游标中一般是自动打开和关闭的,查询都返回False


    17.2使用示例

17.2.1隐式游标

由Oracle在内部声明,数据库自动创建,管理。主要用途是用于增删改数据时,可以返回一个操作成功或失败的相关信息,名称是(sql)

示例:访问游标的属性
/*
 创建员工表,插入测试数据
*/
--drop table employee;
CREATE TABLE employee 
(    
     emp_no CHAR(8) PRIMARY KEY NOT NULL,   --工号,主键,非空
     emp_name VARCHAR2(30) NOT NULL,--姓名,非空      
     emp_id VARCHAR2(18), --身份证号,代表18位整数
     emp_age NUMBER(3,0)  --年龄
);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh001','张大','441521199909092111',19);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh002','张二','441521199909092111',19);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh003','张三','441521199909092113',-1);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh004','张四','441521199909092114',18);
commit;

--隐匿游标使用
declare  
begin  
    insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh005','张5','441521199909092115',18);
    --update employee set emp_age=17 where emp_id='441521199909092111' ;
    --delete from  employee  where  emp_id='441521199909092115' ;  
      if sql%found  then
           dbms_output.put_line('已经影响了数据');
      end if;     
      if  sql%notfound  then
           dbms_output.put_line('没有影响数据');
      end if; 
      if  sql%isopen  then --隐式游标自动打开和关闭的,此时查询已经关闭
              dbms_output.put_line('游标已打开 ' );
      end if; 
      
      dbms_output.put_line('受影响行数 : ' || sql%rowcount); 

end;  

 17.2.1显式游标

使用步骤:
1.在declare 声明游标:Cursor 游标名 is 查询语句(可以多个查询字段);
2.打开游标:open 游标名;
3.提取游标: fetch 游标名 into 变量1,变量2(变量的个数对应字段数);
4.关闭游标:close 游标名;
示例:
/*
 创建员工表,插入测试数据
*/
--drop table employee;
CREATE TABLE employee 
(    
     emp_no CHAR(8) PRIMARY KEY NOT NULL,   --工号,主键,非空
     emp_name VARCHAR2(30) NOT NULL,--姓名,非空      
     emp_id VARCHAR2(18), --身份证号,代表18位整数
     emp_age NUMBER(3,0)  --年龄
);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh001','张大','441521199909092111',19);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh002','张二','441521199909092111',18);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh003','张三','441521199909092113',-4);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh004','张四','441521199909092114',17);
commit;

--使用显式游标遍历(loop),查询并输出所有员工的工号和姓名
declare  
       s_no employee.emp_no%type;
       s_name employee.emp_name%type;
       Cursor emp_cursor is select emp_no,emp_name from employee;  --定义游标
begin  
      open emp_cursor;--打开游标
      loop 
           fetch emp_cursor into s_no,s_name;--提取游标
              exit when emp_cursor%notfound;
           dbms_output.put_line('工号: ' || s_no||'--姓名: ' ||s_name); 

      end loop;
      close emp_cursor;--关闭游标
end;  

/*
使用显式游标(for),查询并输出所有员工的工号和姓名,
for循环游标会隐式打开游标、自动创建%ROWTYPE类型变量对应记录行,
处理完所有行后会自动关闭游标.使用起来较方便。
*/
declare  
       Cursor emp_cursor is select emp_no,emp_name from employee;
begin
      for emp_info in emp_cursor  loop  
           dbms_output.put_line('工号: ' || emp_info.emp_no||'--姓名: ' ||emp_info.emp_name); 
      end loop;
end; 

--使用显式游标修改数据,把年龄为负的员工年龄改为18岁,其它都加一岁
declare
      Cursor emp_cursor is select emp_no,emp_age from employee  for update;
      s_age number(3,0);
      s_no char(8);
begin
      for emp_info in emp_cursor loop
        s_no:=emp_info.emp_no;
        s_age:=emp_info.emp_age;
        if s_age<0 then
          update employee 
               set emp_age=18 where emp_no=s_no;
        else
           update employee 
               set emp_age=s_age+1 where emp_no=s_no;
        end if;
           
      end loop;
end;

18 存储过程和存储函数

    * 参考链接:https://www.2cto.com/database/201802/723493.html

    * 存储过程:属于已命名的pl/sql程序块,封装数据业务操作,具有模块化、可重用、可维护、更安全特点。

    18.1存储过程类型

 1.不带参数
 2.带输入参数
 3.带输出参数
 4.带输入输出参数

18.2存储过程使用

   1创建与调用语法

18.2.

CREATE [OR REPLACE] PROCEDURE procedure_name[(param_list)]
    IS|AS
[DECLARE]
BEGIN
   执行语句;
[EXCEPTION]
    异常处理;
END[procedure_name];
说明:
OR REPLACE:如果系统已存在该存储过程,将被替换
procedure_name:存储过程名称
param_list:参数列表,参数不需要声明长度,可选
DECLARE:局部声明,可选

调用语法
命令行调用:
exec[ute] procedure_name(paramlist);
pl/sql块调用:
begin
procedure_name(paramlist);
end;

 18.2.2准备测试表及数据

/*
 创建员工表,插入测试数据
*/
--drop table employee;
CREATE TABLE employee 
(    
     emp_no CHAR(8) PRIMARY KEY NOT NULL,   --工号,主键,非空
     emp_name VARCHAR2(30) NOT NULL,--姓名,非空      
     emp_id VARCHAR2(18), --身份证号,代表18位整数
     emp_age NUMBER(3,0)  --年龄
);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh001','张大','441521199909092111',19);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh002','张二','441521199909092111',18);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh003','张三','441521199909092113',-4);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh004','张四','441521199909092114',17);
commit;

 18.2.3无参存储过程使用示例

--新建存储过程(不带参数),可以新增员工信息
CREATE OR REPLACE PROCEDURE pro_add_employee
 is
BEGIN
 insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh005','张五','441521199909092115',17);
commit;
END;
/
--调用存储过程
begin
   pro_add_employee;
end;

 18.2.4有输入参数存储过程使用

--新建存储过程(带输入参数,输入参数类型in可以省略不写),可以新增员工信息
CREATE OR REPLACE PROCEDURE pro_add_employee(
       s_no employee.emp_no%type,
       s_name employee.emp_name%type,
       s_id  employee.emp_id%type,
       s_age  employee.emp_age%type
       )
 is
BEGIN
  insert into employee(emp_no,emp_name,emp_id,emp_age) values(s_no,s_name,s_id,s_age);
commit;
END;
/
--调用存储过程
begin
  --按位置传递参数
   pro_add_employee('gh006','张六','441521199909092116',18);
   --按名称传递参数,顺序可变
   pro_add_employee(s_no=>'gh007',s_age=>18,s_name=>'张七',s_id=>'441521199909092117');
   --混合方式(不建议使用),从左开始,第一个没按位置传值的时候得开始指出名称
   pro_add_employee('gh008','张八',s_age=>18,s_id=>'441521199909092118');
end;

 18.2.5有输出参数存储过程使用

--新建存储过程(带输出参数),可以新增员工信息
CREATE OR REPLACE PROCEDURE pro_add_employee(
     s_flag out number,
     s_message out varchar2
     )
 is
BEGIN
 insert into employee(emp_no,emp_name,emp_id,emp_age) values('a010','张10','441521199909092119',17);
commit;
 s_flag:=1;
 s_message:='添加成功!';
   EXCEPTION
    WHEN DUP_VAL_ON_INDEX THEN
         s_flag:=-20001;
          s_message:='工号已经被使用,请核查!';
   WHEN OTHERS THEN
     s_flag:=SQLCODE;
     s_message:=SQLERRM;
END;
/
--调用存储过程
declare
p_flag number(10,0);
p_message varchar2(50);
begin
   --pro_add_employee(p_flag,p_message);
   pro_add_employee(s_message=>p_message,s_flag => p_flag);
   dbms_output.put_line(p_flag||':'||p_message);
end;

1.8.2.6有输入输出参数存储过程使用

--新建存储过程(带输入输出参数,输入参数类型in可以省略不写),可以新增员工信息
CREATE OR REPLACE PROCEDURE pro_add_employee(
       s_no employee.emp_no%type,
       s_name employee.emp_name%type,
       s_id  employee.emp_id%type,
       s_age  employee.emp_age%type,
       s_flag out number,
       s_message out varchar2
       )
 is
BEGIN
  insert into employee(emp_no,emp_name,emp_id,emp_age) values(s_no,s_name,s_id,s_age);
commit;
  s_flag:=1;
  s_message:='保存成功!';
  EXCEPTION
    WHEN DUP_VAL_ON_INDEX THEN
      s_flag:=-20001;
      s_message:='工号已经存在,请核查!';
    WHEN OTHERS THEN
      s_flag:=sqlcode;
      s_message:=sqlerrm;
END;
/
--调用存储过程
declare
p_flag number(10,0);
p_message varchar2(50);
begin
  --按位置传递参数
   pro_add_employee('gh011','张11','111521199909092116',18,p_flag,p_message);
   dbms_output.put_line(p_flag||':'||p_message);
end;

16.2.7基于jdbc的存储过程调用

关键语法:
CallableStatement call=conn.prepareCall("{call 存储过程名(?,?,?,?,?,?)}");
新建项目导入相关数据库驱动包,以下为关键代码:
	@Test
	public void testProc1() throws Exception{
		Class.forName("oracle.jdbc.driver.OracleDriver");
		// 2 获得链接:
		String url="jdbc:oracle:thin:@192.168.10.119:1521:orcl";// ip+port+数据库实例
		String user="scott";
		String password="tiger";
		Connection conn = DriverManager.getConnection(url, user, password);
		CallableStatement call = null;
		  // employee表数据
        call = conn.prepareCall("{call pro_add_employee(?,?,?,?,?,?)}");
        // emp_no CHAR(4) PRIMARY KEY NOT NULL, --工号,主键,非空
        // emp_name VARCHAR2(30) NOT NULL,--姓名,非空
        // emp_id VARCHAR2(18), --身份证号,代表18位整数
        // emp_age NUMBER(3,0) --年龄
        call.setString(1, "gh202");
        call.setString(2, "张202");
        call.setString(3, "88888888888888");
        call.setInt(4, 30);

        // 注册输出型变量
        call.registerOutParameter(5, oracle.jdbc.OracleTypes.NUMBER);
        call.registerOutParameter(6, oracle.jdbc.OracleTypes.VARCHAR);
        call.execute();
        // 获取返回值
        Integer flag = call.getInt(5);
        String message = call.getString(6);
        System.out.println(flag + " " + message);
        
        call.close();
		conn.close();
	}

18.3存储过程的删除

--删除存储过程
drop procedure pro_add_employee;

 18.4存储函数
       * 函数的主要任务是在数据库层编写业务逻辑,但是和存储过程不同的是函数中可以写return子句。

        18.4.1语法  

  create or replace funciton 函数名 [(函数列表)]
    return 数据类型
  as
    定义变量;
  begin
    执行语句;
    return 返回的值;
  end;

  18.4.2使用示例

/*
 准备工作:创建员工表,插入测试数据
*/
--drop table employee;
CREATE TABLE employee 
(    
     emp_no CHAR(8) PRIMARY KEY NOT NULL,   --工号,主键,非空
     emp_name VARCHAR2(30) NOT NULL,--姓名,非空      
     emp_id VARCHAR2(18), --身份证号,代表18位整数
     emp_age NUMBER(3,0)  --年龄
);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh001','张大','441521199909092111',19);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh002','张二','441521199909092111',18);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh003','张三','441521199909092113',20);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh004','张四','441521199909092114',17);
commit;


/*
示例1:查询指定工号的员工年龄
定义存存函数 fun_get_age,参数:工号编号 s_no,类型%type
返回年龄result,number类型
*/
create or replace function fun_get_age(
          s_no employee.emp_no%type
       )
       return number
as
       result number;
begin
       select emp_age into result from employee where emp_no=s_no;
       return result;
end;
/
--调用
 select fun_get_age('gh002') from dual;

/*
 示例2:查询返回指定工号的员工姓名及年龄,并计算和返回员工的平均年龄
 分析:定义函数fun_get_avg_age,入参:工号为s_no,输出参数信息:姓名:s_name,年龄:s_age,返回值:平均年龄:result number(4,1)

*/
 create or replace function fun_get_avg_age(
          s_no employee.emp_no%type,
          s_name out employee.emp_name%type,
          s_age out employee.emp_age%type
       )
       return number
as
       result number(4,1);
begin
       select emp_name,emp_age into s_name,s_age from employee where emp_no=s_no; 
       select avg(emp_age) into result from employee;
       return result;
end;
/ 

--调用
declare
 s_no CHAR(8):='gh001';
 s_name VARCHAR2(30);
 s_age NUMBER(3,0);
 s_avg_age number;

begin
 s_avg_age:=fun_get_avg_age(s_no,s_name,s_age);
 dbms_output.put_line('姓名:'||s_name||',年龄:'||s_age||',平均年龄:'||s_avg_age);
end;

 18.4.3基于jdbc的调用

新建 项目并导入数据库驱动包

关键代码如下:
	@Test
	public void testFunc() throws Exception{
		Class.forName("oracle.jdbc.driver.OracleDriver");
		// 2 获得链接:
		String url="jdbc:oracle:thin:@192.168.10.119:1521:orcl";// ip+port+数据库实例
		String user="scott";
		String password="tiger";
		Connection conn = DriverManager.getConnection(url, user, password);
		CallableStatement call = null;
		 String sql = "{?=call fun_get_age(?)}";
		  // employee表数据
		call=conn.prepareCall(sql);
        call.registerOutParameter(1, oracle.jdbc.OracleTypes.NUMBER);
        call.setString(2, "gh001");
        call.execute();
        int empAge=call.getInt(1);        
        System.out.println("工号:"+"gh001"+",年龄:"+empAge);
        call.close();
		conn.close();
	}
    
    /* 
     * 用于测试存储函数,查询并返回指定工号的员工姓名、年龄及计算返回所有员工的平均年龄
     * (数据库存存函数信息:存存函数名:fun_get_avg_age;入参:s_no 表示工号;输出参数信息:s_name 表示姓名,s_age 表示年龄;返回值:result number(4,1) 表示平均年龄
     */
    
   @Test
	public void testFunc2() throws Exception{
		Class.forName("oracle.jdbc.driver.OracleDriver");
		// 2 获得链接:
		String url="jdbc:oracle:thin:@192.168.10.119:1521:orcl";// ip+port+数据库实例
		String user="scott";
		String password="tiger";
		Connection conn = DriverManager.getConnection(url, user, password);
		CallableStatement call = null;
		String sql = "{?=call fun_get_avg_age(?,?,?)}";
		  // employee表数据
		call=conn.prepareCall(sql);
        call.registerOutParameter(1, oracle.jdbc.OracleTypes.NUMBER);
        call.setString(2, "gh001");
        call.registerOutParameter(3, oracle.jdbc.OracleTypes.VARCHAR);
        call.registerOutParameter(4, oracle.jdbc.OracleTypes.NUMBER);
        call.execute();
        Double empAvgAge = call.getDouble(1);    
        String empName = call.getString(3);
        int empAge = call.getInt(4);
        System.out.println("工号:"+"gh001"+",姓名:"+empName+",年龄:"+empAge+",平均年龄:"+empAvgAge);
        call.close();
		conn.close();
	}
}

 18.5 程序包对象
    * 程序包其实本质就是一个封装的过程,主要用于封装存储过程。包一般分为两个:一个包 一个包体
    18.5.1语法   

创建包:
  CREATE OR REPLACE
   PACKAGE 包名 AS  
    变量声明或声明包中存储过程(procedure 存储过程名[(参数列表)]; 
   END 包名;

创建包体:
CREATE OR REPLACE
 PACKAGE BODY 包名 AS
    procedure 存储过程名[(参数列表)]  AS
    BEGIN
      -- 开始业务,存储过程业务处理
      sql等;
    END 存储过程名;

 END 包名;

  18.5.2使用示例1(添加员工信息)

新建包及包体
新建程序包,实现添加员工逻辑,并进行测试
/*
准备工作,新建表,插入测试数据
*/
--drop table employee;
CREATE TABLE employee 
(    
     emp_no CHAR(8) PRIMARY KEY NOT NULL,   --工号,主键,非空
     emp_name VARCHAR2(30) NOT NULL,--姓名,非空      
     emp_id VARCHAR2(18), --身份证号,代表18位整数
     emp_age NUMBER(3,0)  --年龄
);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh001','张大','441521199909092111',19);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh002','张二','441521199909092111',18);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh003','张三','441521199909092113',20);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh004','张四','441521199909092114',17);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh005','张5','441521199909092115',18);
commit;

select * from employee;


/*
 建立程序包,并定义添加员工信息的存储过程(只声明,不用实现):pro_add_employee,输入参数为员工表各字段。
 输出参数为 s_flag out number,表示操作编号;s_message out varchar2表示详细的操作结果信息
*/
CREATE OR REPLACE
 PACKAGE PAC1 AS
   -- 定义一个抽象的存储过程
   procedure pro_add_employee(
       s_no employee.emp_no%type,
       s_name employee.emp_name%type,
       s_id  employee.emp_id%type,
       s_age  employee.emp_age%type,
       s_flag out number,
       s_message out varchar2
       );
 END PAC1;


/*
新建包体,并编写存储过程的业务,往数据表中添加一条员工记录,并把操作结果信息存放于输出参数中
*/
CREATE OR REPLACE
 PACKAGE BODY PAC1 AS
    procedure  pro_add_employee(
       s_no employee.emp_no%type,
       s_name employee.emp_name%type,
       s_id  employee.emp_id%type,
       s_age  employee.emp_age%type,
       s_flag out number,
       s_message out varchar2
       )
       is
       BEGIN
          insert into employee(emp_no,emp_name,emp_id,emp_age) values(s_no,s_name,s_id,s_age);
          commit;
          s_flag:=1;
          s_message:='保存成功!';
          EXCEPTION
            WHEN DUP_VAL_ON_INDEX THEN
              s_flag:=-20001;
              s_message:='工号已经存在,请核查!';
            WHEN OTHERS THEN
              s_flag:=sqlcode;
              s_message:=sqlerrm;
       END pro_add_employee;
 END PAC1;

--调用存储过程
declare
p_flag number(10,0);
p_message varchar2(50);
begin
   pac1.pro_add_employee('a017','张17','111521199909092116',18,p_flag,p_message);
   dbms_output.put_line(p_flag||':'||p_message);
end;

基于JDBC调用
/* 
     * 用于测试程序包中的存储过程,保存指定的员工信息
     * (数据库程序包名: PAC1;定义的存储过程 :  procedure pro_add_employee(
       s_no employee.emp_no%type,
       s_name employee.emp_name%type,
       s_id  employee.emp_id%type,
       s_age  employee.emp_age%type,
       s_flag out number,
       s_message out varchar2
       );
     */
    
    @Test
	public void testPack() throws Exception{
		Class.forName("oracle.jdbc.driver.OracleDriver");
		// 2 获得链接:
		String url="jdbc:oracle:thin:@192.168.10.119:1521:orcl";// ip+port+数据库实例
		String user="scott";
		String password="tiger";
		Connection conn = DriverManager.getConnection(url, user, password);
		CallableStatement call = null;
		String sql = "{call pac1.pro_add_employee(?,?,?,?,?,?)}";
		 call=conn.prepareCall(sql);
         call.setString(1, "gh021");
         call.setString(2, "张21");
         call.setString(3, "44152119999999999");
         call.setInt(4,22);

         //注册为输出参数
         call.registerOutParameter(5, oracle.jdbc.OracleTypes.NUMBER);
         call.registerOutParameter(6, oracle.jdbc.OracleTypes.VARCHAR);

         call.execute();
         
         int flag = call.getInt(5);
         String message = call.getString(6);
         System.out.println("保存结果信息:"+flag+"||"+message);
        call.close();
		conn.close();
	}
    
18.5.3使用示例2(包中定义多个存储过程)
新建包及包体
--定义包并声明添加员工和根据员工编号查询员工年龄的两个存储过程
create or replace package pac1
as
procedure pro_add_emp(
s_no employee.emp_no%type,
s_name employee.emp_name%type,
s_id employee.emp_id%type,
s_age employee.emp_age%type,
s_flag out number,
s_message out varchar2
);
procedure get_emp_age(
  s_no employee.emp_no%type,
  s_age out employee.emp_age%type
);
end pac1;
--新建包体,并实现两个存储过程的业务
create or replace package body pac1
as
    procedure pro_add_emp(
        s_no employee.emp_no%type,
        s_name employee.emp_name%type,
        s_id employee.emp_id%type,
        s_age employee.emp_age%type,
        s_flag out number,
        s_message out varchar2
      ) 
      as
      begin
        insert into employee(emp_no,emp_name,emp_id,emp_age) values(s_no,s_name,s_id,s_age);
        commit;
        s_flag:=-20003;
        s_message:='添加成功!';
        exception 
          when others then
            s_flag:=-20004;
            s_message:='添加失败!';
    
    end pro_add_emp;
    
    procedure get_emp_age(
        s_no employee.emp_no%type,
        s_age out employee.emp_age%type
      )
      as
        my_age employee.emp_age%type;
      begin
        select emp_age into my_age from employee where emp_no=s_no;      
        s_age:=my_age;
      end get_emp_age;

end pac1;

--调用包中的存储过程,添加员工信息
declare
s_flag1 number(10,2);
s_message1 varchar2(30);
begin
  pac1.pro_add_emp('aa04','aaaa333','5332432432343',21,s_flag1,s_message1);
  dbms_output.put_line(s_flag1||'||'||s_message1);
end;

--调用包中的存储过程,查询员工年龄
declare
s_age number(3);
begin
  pac1.get_emp_age('gh004',s_age);
  dbms_output.put_line(s_age);
end;

 18.6存储过程与存储函数的区别

1. 返回值的区别,函数一定要有1个返回值或有多个通过输出参数的返回值,而存储过程是通过输出参数返回的,可以有多个或者没有。
2.调用的区别,函数可以在sql语句中直接调用,而存储过程必须单独调用.
函数一般情况下是用来计算并返回一个计算结果,而存储过程一般是用来完成特定的数据操作(比如修改、插入数据库表或执行某些DDL语句等等)

19 触发器

     *触发器是在事件发生时隐式地自动运行的PL/SQL程序块,不能接收参数,不能被调用。,  一般监听的事件是数据的增删改事件。 

简单语法:
CREATE [OR REPLACE] TRIGGER  trigger_name
  {BEFORE|AFTER}    --触发时机
  {INSERT|DELETE|UPDATE
    ON{table_name }
  [FOR EACH ROW]
  trigger_body; 
备注:trigger _body就是pl/sql块

 19.1示例练习(备份删除的数据) 

删除员工信息时,同时把要删除员工信息备份到另一张表中,以防止误操作时数据丢失或便于以后对数据的查找恢复。
-- 1.准备工作:创建员工表,插入测试数据
--drop table employee;
--drop table emp_del_rec;
CREATE TABLE employee 
(    
     emp_no number(4,0) PRIMARY KEY,   --工号,主键,非空
     emp_name VARCHAR2(30) NOT NULL,--姓名,非空      
     emp_id VARCHAR2(18), --身份证号,代表18位整数
     emp_age NUMBER(3,0)  --年龄
);
CREATE TABLE emp_del_rec 
(    
     emp_no number(4,0)  NOT NULL,   --工号,非空
     emp_name VARCHAR2(30) NOT NULL,--姓名,非空      
     emp_id VARCHAR2(18), --身份证号,代表18位整数
     emp_age NUMBER(3,0),  --年龄
     emp_operator_date date--操作日期
);
insert into employee(emp_no,emp_name,emp_id,emp_age) values(1001,'张大','441521199909092111',19);
insert into employee(emp_no,emp_name,emp_id,emp_age) values(1002,'张二','441521199909092111',18);
insert into employee(emp_no,emp_name,emp_id,emp_age) values(1003,'张三','441521199909092113',-4);
insert into employee(emp_no,emp_name,emp_id,emp_age) values(1004,'张四','441521199909092114',17);
commit;

--2.创建触发器,删除数据时可以把删除的数据备份到其它表中
create or replace trigger tri_emp_del_rec
   before delete on employee for each row
 declare
 begin
       --:old.列名,可以引用在增删改前原表字段数据,没有数据为空;:new.列名,可以引用在增删改后字段新值,处理后对应记录不存在,则数据为空
       insert into emp_del_rec(emp_no,emp_name,emp_id,emp_age,emp_operator_date) values(:old.emp_no,:old.emp_name,:old.emp_id,:old.emp_age,sysdate);
 end;
/

--3.测试触发器,删除数据及观察结果
delete from employee where emp_no=1003;
select * from emp_del_rec;
select * from employee;

 19.2示例练习(实现主键自动增长)

--通过触发器+序列完自动增长
--准备工作,继续使用上面的员工表
--1.创建序列
create  sequence seq_emp_no;
--2.创建触发器
create or replace trigger tri_gen_pk
 before insert on employee for each row       
begin
select seq_emp_no.nextval into :new.emp_no from dual;
end;
/
--3.测试触发器
insert into employee(emp_name,emp_id,emp_age) values('张三','441521199909092113',-4);
insert into employee(emp_name,emp_id,emp_age) values('张四','441521199909092114',17);
select * from employee;

 19.3示例练习(表级触发器,删除时整表备份)

表级触发器里不能再用:old或:new。
/*
 创建员工表,插入测试数据
*/
--drop table employee;
--drop table emp_del_rec;
CREATE TABLE employee 
(    
     emp_no number(4,0) PRIMARY KEY NOT NULL,   --工号,主键,非空
     emp_name VARCHAR2(30) NOT NULL,--姓名,非空      
     emp_id VARCHAR2(18), --身份证号,代表18位整数
     emp_age NUMBER(3,0)  --年龄
);
CREATE TABLE emp_del_rec 
(    
     emp_no number(4,0)  NOT NULL,   --工号,非空
     emp_name VARCHAR2(30) NOT NULL,--姓名,非空      
     emp_id VARCHAR2(18), --身份证号,代表18位整数
     emp_age NUMBER(3,0),  --年龄
     emp_operator_date date default sysdate --操作日期
);
insert into employee(emp_no,emp_name,emp_id,emp_age) values(1001,'张大','441521199909092111',19);
insert into employee(emp_no,emp_name,emp_id,emp_age) values(1002,'张二','441521199909092111',18);
insert into employee(emp_no,emp_name,emp_id,emp_age) values(1003,'张三','441521199909092113',-4);
insert into employee(emp_no,emp_name,emp_id,emp_age) values(1004,'张四','441521199909092114',17);
commit;

--创建触发器,删除数据时可以把删除的数据备份到其它表中,要使用before,不能用after
create or replace trigger tri_emp_del_rec
   before delete on employee
 declare
 begin
       Insert into emp_del_rec(emp_no,emp_name,emp_id,emp_age) select emp_no,emp_name,emp_id,emp_age from employee;
end;
/

--测试触发器,删除数据及观察结果
delete from employee;
select * from emp_del_rec;
select * from employee;

20 视图

    * 参考链接:https://www.cnblogs.com/easypass/archive/2010/08/21/1805570.html

    * Oracle视图作为原始的相关数据库中的数据变换,它的主要功能是查看相关表中的某些数据的另外的方式。可以将Oracle视图看成是一个移动的窗口,通过它可以看到感兴趣的数据。

     20.1 视图简介

    也称虚表, 不占用物理空间,这个也是相对概念,因为视图本身的定义语句还是要存储在数据字典里的。视图只有逻辑定义。每次使用的时候只是重新执行SQL.
    视图是从一个或多个实际表中获得的,这些表的数据存放在数据库中。那些用于产生视图的表叫做该视图的基表。一个视图也可以从另一个视图中产生。
    create view 视图名 as 查询语句;
    drop view 视图名;
    表的列经常被进行查询,可以考虑建立视图。

  20.2视图使用示例

--drop table employee;
--drop table my_class;
CREATE TABLE employee
(    
     emp_no CHAR(8) PRIMARY KEY NOT NULL,   --工号,主键,非空
     emp_name VARCHAR2(30) NOT NULL,--姓名,非空      
     emp_id VARCHAR2(18), --身份证号,代表18位整数
     mc_no CHAR(4)  --班级编号
);
CREATE TABLE my_class
(    
     mc_no CHAR(5) PRIMARY KEY NOT NULL,   --编号,主键,非空
     mc_name VARCHAR2(30) NOT NULL--名称,非空      
);
insert into my_class(mc_no,mc_name) values('c001','软件一班');
insert into my_class(mc_no,mc_name) values('c002','软件二班');
insert into employee(emp_no,emp_name,mc_no) values('gh001','张一','c001');
insert into employee(emp_no,emp_name,mc_no) values('gh002','张二','c001');
insert into employee(emp_no,emp_name,mc_no) values('gh003','张三','c002');
commit;
 
--登陆system用户,给用户scott授创建视图的权限
grant create view to scott;
--登陆scott用户,创建视图
create or replace view view_employee
as 
select emp_no,emp_name from employee ;

--通过视图查询数据
select * from view_employee;

--通过视图添加数据,需要保证基表的其它数据项可以为空
insert into view_employee(emp_no,emp_name) values('gh007','张七');

--通过视图修改数据
update view_employee set emp_name='张2' where emp_no='gh007'

--通过视图删除数据
delete from view_employee  where emp_no='gh007'

--基于多个基表的视图,不建议使用视图进行增删改操作
create or replace view view_my_class_employee
as 
select mc.mc_name,s.emp_no,s.emp_name from employee s inner join my_class mc on s.mc_no=mc.mc_no;

--查询多个基表的视图
select * from view_my_class_employee;

--创建基于视图的视图
create or replace view view_view_employee
as 
select emp_no from view_employee;

--查询基于视图的视图
select * from view_view_employee;

--删除视图
drop view view_view_employee;
drop view view_my_class_employee;
drop view view_employee;

 21 数据闪退

21.1简介
oracle可以在删除之后进行数据和表对象的闪回,任何闪回技术和恢复技术都是基于系统的某一个时间点的,因此oracle的恢复和闪回技术也是一样,
    SCN: System Change Number  和 系统的时间值一一对应,SCN可以作为恢复的时间点。
-- 查看当前时刻的SCN号?
 select to_char(sysdate,'yyyy-mm-dd hh24:mi:ss'), timestamp_to_scn(sysdate) from dual;

-- 使用管理员sys,用dba角色查看undo表空间的参数(在sqlplus运行语句)
 /*
 运行结果:
SQL> show parameter undo
 
NAME                                 TYPE        VALUE
------------------------------------ ----------- 
undo_management                      string      AUTO
undo_retention                       integer     900
undo_tablespace                      string      UNDOTBS1  
默认的情况下有效的闪回时间为900(15分钟),但是管理员可以通过设置语句来修改闪回的有效值时间。 单位是 秒 60
  
 -- 修改当前系统的undo表空间的参数(可以不修改)
 alter system set undo_retention=2800 scope=both;
 
21.2闪回示例
闪回删除的表数据 
/*
创建员工表,及测试数据
*/
--drop table employee;
CREATE TABLE employee
(    
     emp_no CHAR(8) PRIMARY KEY NOT NULL,   --工号,主键,非空
     emp_name VARCHAR2(30) NOT NULL,--姓名,非空      
     emp_id VARCHAR2(18), --身份证号,代表18位整数
     emp_age NUMBER(3,0)  --年龄
);

insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh001','张大','441521199909092111',19);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh002','张二','441521199909092112',19);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh003','张三','441521199909092113',18);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh004','张四','441521199909092114',18);
commit;
--查看并记录当前的SCN号
select to_char(sysdate,'yyyy-mm-dd hh24:mi:ss'), timestamp_to_scn(sysdate) from dual;--S例如SCN号:2765382
--错误删除数据
delete employee where emp_no='gh003';
commit;
--查看表记录,emp_no为gh003数据已被删除
select * from employee;

--闪回刚才删除数据,需要先修改表的列可以移动: 登陆sys用户:alter table user_it.employee enable row movement;
flashback table employee to scn 2765382;
--查看数据,发现已被恢复
 select * from employee;

22 数据备份与恢复

    22.1数据备份

-- 登录user_hx 用户
--drop table employee;
CREATE TABLE employee
(    
     emp_no CHAR(8) PRIMARY KEY NOT NULL,   --工号,主键,非空
     emp_name VARCHAR2(30) NOT NULL,--姓名,非空      
     emp_id VARCHAR2(18), --身份证号,代表18位整数
     emp_age NUMBER(3,0)  --年龄
);
select * from employee;

insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh001','张大','441521199909092111',19);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh002','张二','441521199909092112',19);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh003','张三','441521199909092113',18);
insert into employee(emp_no,emp_name,emp_id,emp_age) values('gh004','张四','441521199909092114',18);
commit;

命令行:
  完整备份
    exp user_hx/123123@orcl  file=c:\oracle_bak.dmp full=y 
    指定表备份
    exp user_hx/123123@orcl  file=c:\oracle_employee_bak.dmp tables=(employee)

22.2恢复

导出后可以删除表,再恢复
drop table employee
指定表恢复
    imp  user_hx/123123@orcl  ignore=y file=c:\oracle_employee_bak.dmp tables=(employee)
导出后可以删除用户及表空间
select username,default_tablespace,temporary_tablespace from dba_users where username='USER_HX';
drop user user_hx cascade;
drop tablespace HXDATA3 including contents and datafiles;

再重新创建用户及表空间:

-- 2.创建一个自动增长的表空间ts_it,AUTOEXTEND ON不用时不会自动增长
CREATE TABLESPACE HXDATA5 
DATAFILE 'c:\ORACLE_DATA\HXDATA5.DBF' 
SIZE 10M AUTOEXTEND ON;
/
-- 3.创建用户user_hx,下面的user可以改为指定为上面建的表空间HXDATA5
CREATE USER  user_hx
IDENTIFIED BY 123123
DEFAULT TABLESPACE HXDATA5
TEMPORARY TABLESPACE TEMP;
/
--4.授予CONNECT和RESOURCE两个角色
GRANT connect, resource TO user_hx;
grant create view to user_hx;
grant create synonym to user_hx;
grant create public synonym to user_hx;
    完整恢复,命令行执行如下语句:
    imp user_hx/123123@orcl  ignore=y file=c:\oracle_bak.dmp  full=y

* 备注:

* 参考链接:https://www.cnblogs.com/qneverever/p/4500023.html

* Oracle服务端配置

    * https://blog.csdn.net/tianlesoftware/article/details/4861572

*  PLSQL Developer 远程配置

    * https://blog.csdn.net/laowang2915/article/details/74915032

猜你喜欢

转载自blog.csdn.net/linzhaoliangyan/article/details/88557374