总结:Oracle快速入门

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

一:数据库分类

小:access/foxbase等

中:mysql/sql server/informix等

大:oracle/sybase/db2

二:sqlpus常用命令

oracle自带用户:sys(超级用户,具有sysdba角色,可以创建数据库)、system(具有sysoper角色),区别在于是否能创建数据库。

1:连接命令:【conn 用户名/密码@网络服务名 [as sysdba/sysoper]】

2:显示当前用户:【show user】

3:断开连接:【disc/disconn/disconnect】

4:修改当前用户的密码:【passw/password】,修改其他用户密码(权限):【alter user 用户名 identity by 新密码】

5:退出sqlplus:【exit】

6:清空屏幕:【clear screen】

7:运行sql脚本:【start/@ url.sql】

8:编辑sql脚本:【edit url.sql】

9:将屏幕内容输入到指定文件:【spool url.sql】【spool off】

10:交互式命令&:【&value】需要用户输入值

11:显示和设置环境变量:【show/set linesize/pagesize】

12:创建用户(dba权限):create user 用户名 identified by 密码;

13:删除用户:【drop user 用户名 [cascade]】

14:授予权限(系统和对象权限):【grant 权限 to 用户 [with grant option]/[with admin opyion]】

15:收回权限:【revoke 权限 on 对象 from 用户】(系统权限不级联,对象权限级联收回)

16:账号锁定(创建profile口令限制的集合):【create profile lock_account limit failed_login_attempts 3 password_lock_time 2;】【alter user scott profile lock_account;】

17:账户解锁:【alter user scott account unlock;】

18:终止口令(使用户定期修改密码):【create profile myprofile limit password_life_time 10 password_grace_time 2;】【alter user test profile myprofile;】

19:口令历史:【create profile password_history limit password_life_time 10 password_grace_time 2 password_reuse_time 10】【alter user test profile password_history;】

20:删除profile:【drop profile password_history [casade]】

三:常用SQL命令

1:数据类型:char/varchar2(n)/varchar/number(n,m)/date/timestamp/blob

2:创建表:create table demo( filed1 varchar2(20),...);

3:修改表:alter table demo add/modify ( filed1 varchar2(20))/drop column field1;

4:修改表名字:rename demo to test;

5:删除表:drop table demo;

6:添加数据:insert into table demo values(...);

7:修改数据:update demo set ... where ...

8:删除数据:delete from demo where ...;

9:删除表结构和数据:drop table demo;

10:删除所有表记录,无日志写入无法找回删除的记录:truncate table demo;

11:查看表结构:desc demo;

12:查询所有列:select * from demo;

13:底部显示操作时间:set timing on/off;

14:表复制语句:insert into t2(filed1,...) select v1,v2,...from t1;

15:查询统计:select count(*) from demo;

16:查询语句:select [distinct] field1 [as alias],... from ... where ...

17:nvl函数:nvl(str1,replace_str2)

18:连接字符串:||

19:模糊查询:like '%demo_';

20:范围查询:in (A,B,C);

21:判空查询:is null/null

22:升降序排列:order by [asc/desc]

23:聚合函数(如果列里面有一个分组函数,其它都必须是分组函数):max/min/avg/sum/count

24:分组统计:group by ... having ...    (order by ...)

25: 当在from 子句中使用子查询时,必须给子查询指定别名 , 给表取别名的时候,不能加as;但是给列取别名,是可以加as的。

26:根据查询结果创建新表: CREATE TABLE mytable (id, name, sal, job, deptno) as SELECT empno, ename, sal, job, deptno FROM emp;

27:合并查询:union(去掉重复行)/union all/intersect/minus

四:oracel分页方式

1:rowid分页

SELECT *
FROM EMP
WHERE ROWID IN
       (SELECT RID
          FROM (SELECT ROWNUM RN, RID
                  FROM (SELECT ROWID RID, EMPNO FROM EMP ORDER BY EMPNO DESC)
                 WHERE ROWNUM <= ( (currentPage-1) * pageSize + pageSize )) --每页显示几条
         WHERE RN > ((currentPage-1) * pageSize) ) --当前页数
 ORDER BY EMPNO DESC;

2:rownum分页

SELECT *
  FROM (SELECT T.*, ROWNUM RN
          FROM (SELECT * FROM EMP ORDER BY EMPNO DESC) T
         WHERE ROWNUM <= ( (currentPage-1) * pageSize + pageSize )) --每页显示几条
  WHERE RN > ( (currentPage-1) * pageSize ); --当前页数

3:分析函数分页

SELECT *
FROM (SELECT T.*, ROW_NUMBER() OVER(ORDER BY empno DESC) RK FROM emp T)
WHERE RK <= ( (currentPage-1) * pageSize + pageSize ) --每页显示几条
AND RK > ( (currentPage-1) * pageSize ); --当前页数

解析步骤:

1:SELECT * FROM emp;
2:显示rownum,由oracle分配的
SELECT e.*, ROWNUM rn FROM (SELECT * FROM emp) e; --rn相当于Oracle分配的行的ID号
3:先查出1-10条记录
正确的: SELECT e.*, ROWNUM rn FROM (SELECT * FROM emp) e WHERE ROWNUM<=10;
错误的:SELECT e.*, ROWNUM rn FROM (SELECT * FROM emp) e WHERE rn<=10;
4:然后查出6-10条记录
SELECT * FROM (SELECT e.*, ROWNUM rn FROM (SELECT * FROM emp) e WHERE ROWNUM<=10) WHERE rn>=6;

五:事务操作

1:设置保存点:savepoint a;

2:回滚到保存点:rollback to a;

3:回滚全部事务操作:rollback;

4:提交事务:commit;

5:java中使用事务:conn.setAutoCommit(false);关闭自动提交事务,然后事务机制操作

6:只读事务: set transaction read only;

六:常用函数

1:字符函数: lower(char) /upper(char)/length(char)/substr(char, m, n)/replace(char1, search_string, replace_string) /instr(C1,C2,I,J)

2:数字函数: cos,cosh,exp,ln, log,sin,sinh,sqrt,tan,tanh,acos,asin,atan,round 等

3:日期函数: sysdate (返回系统时间 )、 add_months(time,moths)函数 ( 得到某一时间之前或之后n个月的时间 )、 last_day(d)(返回指定日期所在月份的最后一天 )

4:转换函数: to_char(date,formatStr)函数(

yy:两位数字的年份 2004-->04
yyyy:四位数字的年份 2004年
mm:两位数字的月份 8 月-->08
dd:两位数字的天 30 号-->30
hh24: 8点-->20
hh12:8点-->08
mi、ss-->显示分钟\秒
9:显示数字,并忽略前面0
0:显示数字,如位数不足,则用0补齐
.:在指定位置显示小数点
,:在指定位置显示逗号
$:在数字前加美元
L:在数字前面加本地货币符号
C:在数字前面加国际货币符号
G:在指定位置显示组分隔符、
D:在指定位置显示小数点符号(.)

)

to_date()函数 ('dateStr',formatStr)

5:sys_context(‘USERENV’,param)系统函数:(param=teminal/language/db_name/nls_date_format/session_user/currentt_schema/host)

6:管理员区别: sysdba>sysoper>dba

总结:Oracle快速入门_第1张图片

7:显示初始化参数:show parameter

8:修改参数: oracle\admin\myoral\pfile\init.ora 文件中去修改。

七:备份与恢复

一:备份(导出):导出表、导出方案、导出数据库

参数:

userid:用于指定执行导出操作的用户名,口令,连接字符串
tables:用于指定执行导出操作的表
owner:用于指定执行导出操作的方案
full=y:用于指定执行导出操作的数据库
inctype:用于指定执行导出操作的增量类型
rows:用于指定执行导出操作是否要导出表中的数据
file:用于指定导出文件名

1:导出表:

exp userid=scott/oracle@orcl tables=(emp) file=d:\emp.dmp --导出单个表
exp userid=scott/oracle@orcl tables=(emp,dept) file=d:\emp.dmp --导出多个表

导出其他方案的表:

exp userid=system/oracle@orcl tables=(scott.emp) file=d:\emp.emp

exp userid=system/oracle@orcl tables=(scott.emp,scott.dept) file=d:\emp.emp

导出表结构:

exp userid=scott/oracle@orcl tables=(emp) file=d:\emp.dmp rows=n

直接导出方式 :

exp userid=scott/oracle@orcl tables=(emp) file=d:\emp.dmp direct=y

2:导出方案:

导出方案:

exp userid=scott/oracle@orcl owner=scott file=d:\scott.dmp

导出其它方案:

exp userid=system/oracle@orcl owner=(system,scott) file=d:\system.dmp

3:导出数据库

exp userid=system/oracle@orcl full=y inctype=complete file=d:\all.dmp

二:恢复(导入)

参数:

userid:用于指定执行导入操作的用户名,口令,连接字符串
tables:用于指定执行导入操作的表
formuser:用于指定源用户
touser:用于指定目标用户
file 用于指定导入文件名
full=y:用于指定执行导入整个文件
inctype:用于指定执行导入操作的增量类型
rows:指定是否要导入表行(数据)
ignore:如果表存在,则只导入数据

1:导入表

导入表:
imp userid=scott/oracle@orcl tables=(emp) file=d:\xx.dmp
导入表到其它用户:
imp userid=system/oracle@orcl tables=(emp) file=d:\xx.dmp touser=scott
导入表结构:
imp userid=scott/oracle@orcl tables=(emp) file=d:\xx.dmp rows=n
导入数据:
imp userid=scott/oracle@orcl tables=(emp) file=d:\xx.dmp ignore=y 

2:导入方案
导入自身的方案:
imp userid=scott/oracle@orcl file=d:\xxx.dmp
导入其它方案:
imp userid=system/oracle@orcl file=d:\xxx.dmp fromuser=system touser=scott

3:导入数据库(相当于数据库迁移)
imp userid=system/oracle@orcl full=y file=d:\xxx.dmp

八:视图操作

1:数据字典视图: user_xxx,all_xxx,dba_xxx

user_tables: 用于显示当前用户所拥有的所有表,它只返回用户所对应方案的所有表 。

all_tables: 用于显示当前用户可以访问的所有表,它不仅会返回当前用户方案的所有表,还会返回当前用户可以访问的其它方案的表 。

dba_tables: 它会显示所有方案拥有的数据库表。但是查询这种数据库字典视图,要求用户必须是dba角色或是有select any table 系统权限。

2:用户名和权限和角色

dba_users可以显示所有数据库用户的详细信息;
dba_sys_privs可以显示用户所具有的系统权限;
dba_tab_privs可以显示用户具有的对象权限;
dba_col_privs 可以显示用户具有的列权限;
dba_role_privs 可以显示用户所具有的角色。

九:表空间和数据文件

表空间:数据库的逻辑组成部分, 物理上讲,数据库数据存放在数据文件中。逻辑上讲,数据库数据则是存放在表空间中,表空间由一个或多个数据文件组成( 数据库由表空间构成,而表空间又是由段构成,而段又是由区构成,而区又是由oracle块构成 )。

1:建立表空间: create tablespace data01 datafile 'D:\dev\oracle\product\10.2.0\dada01.dbf' size 20m uniform size 128k;

2:使用表空间:

create table mypart(
   deptno number(4),
   dname varchar2(14),
   loc varchar2(13)
) tablespace data01;

3:改变表空间状态:

表空间脱机: alter tablespace 表空间名 offline;

表空间联机: alter tablespace 表空间名 online;

只读表空间: alter tablespace 表空间名 read only;

4:删除表空间: drop tablespace ‘表空间’ including contents and datafiles;

5:扩展表空间:

增加数据文件 : alter tablespace sp01 add datafile 'D:\dev\oracle\product\10.2.0\dada02.dbf' size 1m;

修改数据文件的大小 : alter tablespace sp01 'D:\dev\oracle\product\10.2.0\dada01.dbf' resize 4m;

设置文件的自动增长 : alter tablespace sp01 'D:\dev\oracle\product\10.2.0\dada01.dbf' autoextend on next 10m maxsize 500m;

6:移动数据文件: 确定数据文件所在的表空间 》 使表空间脱机 》 使用命令移动数据文件到指定的目标位置 》 执行alter tablespace 命令 》 使得表空间联机

select tablespace_name from dba_data_files where file_name=upper('D:\dev\oracle\product\10.2.0\dada01.dbf');

alter tablespace sp01 offline;

host move D:\dev\oracle\product\10.2.0\dada01.dbf c:\dada01.dbf;

alter tablespace sp01 rename datafile 'D:\dev\oracle\product\10.2.0\dada01.dbf' to 'c:\sp01.dbf';

alter tablespace sp01 online;

7:显示表空间信息: select tablespace_name from dba_tablespaces;

8: 除了最常用的数据表空间外,还有其它类型表空间:索引表空间、 undo表空间、 临时表空间、 非标准块的表空间。

十:约束

1:约束包括:not null、 unique, primary key, foreign key和check 五种。

1)、not null(非空)
如果在列上定义了not null,那么当插入数据时,必须为列提供数据。
2)、unique(唯一)
当定义了唯一约束后,该列值是不能重复的,但是可以为null。
3)、primary key(主键)
用于唯一的标示表行的数据,当定义主键约束后,该列不但不能重复而且不能为null。
需要说明的是:一张表最多只能有一个主键,但是可以有多个unqiue约束。
4)、foreign key(外键)
用于定义主表和从表之间的关系。外键约束要定义在从表上,主表则必须具有主键约束或是unique 约束,当定义外键约束后,要求外键列数据必须在主表的主键列存在或是为null。
5)、check
用于强制行数据必须满足的条件,假定在sal列上定义了check约束,并要求sal列值在1000-2000之间如果不在1000-2000之间就会提示出错。

2:删除约束

alter table 表名 drop constraint 约束名称 ;

3:显示约束信息

select constraint_name, constraint_type, status, validated from user_constraints where table_name = '表名';

select column_name, position from user_cons_columns where constraint_name = '约束名';

直接用pl/sql developer查看 .

十一:索引

1:创建索引

单列索引:单列索引是基于单个列所建立的索引
语法:create index 索引名 on 表名(列名);

复合索引:复合索引是基于两列或是多列的索引。在同一张表上可以有多个索引,但是要求列的组合必须不同,比如:create index emp_idx1 on emp(ename, job); create index emp_idx1 on emp(job, ename); 这两个索引是两个不同的索引。

2:使用原则

1)、在大表上建立索引才有意义
2)、在where子句或是连接条件上经常引用的列上建立索引
3)、索引的层次不要超过4层

3:索引缺点

1)、建立索引,系统要占用大约为表1.2倍的硬盘和内存空间来保存索引。
2)、更新数据的时候,系统必须要有额外的时间来同时对索引进行更新,以维持数据和索引的一致性。

比如在如下字段建立索引应该是不恰当的:
1. 很少或从不引用的字段;
2. 逻辑型的字段,如男或女(是或否)等。

4:索引分类

按照数据存储方式,可以分为B*树、反向索引、位图索引;
按照索引列的个数分类,可以分为单列索引、复合索引;
按照索引列值的唯一性,可以分为唯一索引和非唯一索引。
此外还有函数索引,全局索引,分区索引...

5:显示索引信息

1):在同一张表上可以有多个索引,通过查询数据字典视图dba_indexs和user_indexs,可以显示索引信息。其中dba_indexs用于显示数据库所有的索引信息,而user_indexs用于显示当前用户的索引信息:select index_name, index_type from user_indexes where table_name = '表名';
2):显示索引列:通过查询数据字典视图user_ind_columns,可以显示索引对应的列的信息
select table_name, column_name from user_ind_columns where index_name ='IND_ENAME';
3):你也可以通过pl/sql developer工具查看索引信息 。

十二:权限

一:系统权限

常用:

create session 连接数据库
create table 建表
create view 建视图
create public synonym 建同义词
create procedure 建过程、函数、包
create trigger 建触发器
create cluster 建簇

1:显示系统权限: select * from system_privilege_map order by name;

2:授予系统权限: grant create session, create table to ken with admin option;

3:回收系统权限(不级联): revoke create session from ken;

二:对象权限

常用:

insert 添加
delete 删除
alter 修改
select 查询
index 索引
references 引用
execute 执行

1:显示对象权限: select distinct privilege from dba_tab_privs;

2:授予对象权限: grant select on emp to blake with grant option;

3:授予列权限: grant select on emp(ename,sal) to monkey ;

4:回收对象权限(级联): revoke select on emp from blake ;

十三:角色

一:预定义角色: connect、resource、dba

1:connect角色
connect角色具有一般应用开发人员需要的大部分权限。
create cluster
create database link
create session
alter session
create table
create view
create sequence

2:resource角色
resource角色具有应用开发人员所需要的其它权限。
resource角色包含以下系统权限:
create cluster
create indextype
create table
create sequence
create type
create procedure
create trigger 

3:dba角色:具有所有的系统权限,及with admin option选项,默认的dba用户为sys和system,它们可以将任何系统权限授予其他用户。但是要注意的是dba角色不具备sysdba和sysoper的特权(启动和关闭数据库)。

二:自定义角色

1:建立角色: create role 角色名 not identified; create role 角色名 identified by 密码;

2:角色授权: grant insert, update, delete on scott.emp to 角色名;

3:分配角色给用户: grant 角色名 to 用户 with admin option;

4:删除角色: drop role 角色名;

5:显示角色信息:

1)、显示所有角色: select * from dba_roles;
2)、显示角色具有的系统权限:select privilege, admin_option from role_sys_privs where role='角色名';
3)、显示角色具有的对象权限:通过查询数据字典视图dba_tab_privs可以查看角色具有的对象权限或是列的权限。
4)、显示用户具有的角色,及默认角色:当以用户的身份连接到数据库时,oracle 会自动的激活默认的角色,通过查询数据字典视图dba_role_privs 可以显示某个用户具有的所有角色及当前默认的角色。
select granted_role, default_role from dba_role_privs where grantee = ‘用户名’;

十四:PL/SQL

1:编码规范

总结:Oracle快速入门_第2张图片

2: pl/sql块: 三个部分构成:定义部分,执行部分,例外处理部分。

declare
/*定义部分——定义常量、变量、游标、例外、复杂数据类型*/
begin
/*执行部分——要执行的pl/sql 语句和sql 语句*/
exception
/*例外处理部分——处理运行的各种错误*/
end;

eg:

set serveroutput on; 
DECLARE
    --定义字符串变量
    v_ename varchar2(10); 
    v_sal NUMBER(7,2);
BEGIN
    --执行部分
    select ename, sal into v_ename, v_sal from emp where empno=&empno; 
    dbms_output.put_line('雇员名:'||v_ename||',薪水:'||v_sal);
EXCEPTION
    --异常处理    
    WHEN no_data_found THEN dbms_output.put_line('朋友,您的编号输入有误!');
end;
/

3:存储过程: 用于执行特定的操作,当建立存储过程时,既可以指定输入参数(in),也可以指定输出参数(out),通过在过程中使用输入参数,可以将数据传递到执行部分;通过使用输出参数,可以将执行部分的数据传递到应用环境。在sqlplus中可以使用create procedure命令来建立过程。

编写存储过程:

CREATE PROCEDURE sp_update(uname VARCHAR2, newsal NUMBER) IS
BEGIN
   update emp set sal=newsal where ename=uname;
END;
/

调用存储过程:

SQL> exec sp_update('zhangsan', 888);
SQL> commit;

java中调用存储过程:

package junit.test;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;

/**
 * 演示java程序调用oracle的存储过程案例
 * 
 * @author jiqinlin
 *
 */
public class ProcedureTest {

    public static void main(String[] args) {

        try {
            // 1.加载驱动
            Class.forName("oracle.jdbc.driver.OracleDriver");
            // 2.得到连接
            Connection ct = DriverManager.getConnection(
                    "jdbc:oracle:thin:@127.0.0.1:1521:orcl", "scott", "oracle");

            // 3.创建CallableStatement
            CallableStatement cs = ct.prepareCall("{call sp_update(?,?)}");
            // 4.给?赋值
            cs.setString(1, "SMITH");
            cs.setInt(2, 4444);
            // 5.执行
            cs.execute();
            // 关闭
            cs.close();
            ct.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

4:函数: 用于返回特定的数据,当建立函数时,在函数头部必须包含return子句。而在函数体内必须包含return语句返回的数据。我们可以使用create function来建立函数。

编写函数:

CREATE FUNCTION annual_incomec(uname VARCHAR2)
RETURN NUMBER IS 
annual_salazy NUMBER(7,2);
BEGIN 
   SELECT a.sal*13 INTO annual_salazy FROM emp a WHERE a.ename=uname;
   RETURN annual_salazy;
END;
/

调用函数:

SQL> var income NUMBER;
SQL> call annual_incomec('SCOTT') into:income;
SQL> print income;

java中调用存储过程:

package junit.test;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

/**
 * 演示java程序调用oracle的函数案例
 * 
 * @author jiqinlin
 *
 */
public class ProcedureTest {

    public static void main(String[] args) {

        try {
            // 1.加载驱动
            Class.forName("oracle.jdbc.driver.OracleDriver");
            // 2.得到连接
            Connection ct = DriverManager.getConnection(
                    "jdbc:oracle:thin:@127.0.0.1:1521:orcl", "scott", "oracle");
            // 3.创建PreparedStatement
            PreparedStatement ps = ct.prepareStatement("select annual_incomec('SCOTT') annual from dual");
            // 4.执行
            ResultSet rs=ps.executeQuery();
            if(rs.next()){
                Float annual=rs.getFloat("annual");
                System.out.println(annual);
            }
            //5、关闭
            rs.close();
            ps.close();
            ct.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

5:包: 用于在逻辑上组合过程和函数,它由包规范和包体两部分组成, 使用create package命令来创建包 。

1:声明包:

create package sp_package is
   procedure update_sal(name varchar2, newsal number);
   function annual_income(name varchar2) return number;
end;

2:创建包体:

CREATE OR REPLACE PACKAGE BODY SP_PACKAGE IS
  --存储过程
  PROCEDURE UPDATE_SAL(NAME VARCHAR2, NEWSAL NUMBER) IS
  BEGIN
     UPDATE EMP SET SAL = NEWSAL WHERE ENAME = NAME;
     COMMIT;
  END;
  
  --函数
  FUNCTION ANNUAL_INCOME(NAME VARCHAR2) RETURN NUMBER IS
     ANNUAL_SALARY NUMBER;
  BEGIN
     SELECT SAL * 12 + NVL(COMM, 0) INTO ANNUAL_SALARY FROM EMP WHERE ENAME = NAME;
     RETURN ANNUAL_SALARY;
  END;
END;
/

调用:

exec sp_package.update_sal('SCOTT', 8888);
--调用函数
var income NUMBER;
CALL sp_package.ANNUAL_INCOME('SCOTT') INTO:income;
print income;

6:变量:

1)、标量类型(scalar)

pl/sql中定义变量和常量的语法如下:
identifier [constant] datatype [not null] [:=| default expr]
identifier: 名称
constant:指定常量。需要指定它的初始值,且其值是不能改变的
datatype:数据类型
not null:指定变量值不能为null
:= 给变量或是常量指定初始值
default 用于指定初始值
expr :指定初始值的pl/sql表达式,可以是文本值、其它变量、函数等。

使用%type类型可以避免数字或值错误( 超过了5个字符的话,就会有“ORA-06502: PL/SQL: 数字或值错误 :  字符串缓冲区太小”错误 ) : 标识符名 表名.列名%type;
2)、复合类型(composite):

pl/sql记录类似结构体:

set serveroutput on; --打开输出选项
DECLARE
  --定义一个pl/sql记录类型emp_record_type,
  --类型包含3个数据NAME, SALARY, TITLE。说白了,就是一个类型可以存放3个数据,主要是为了方便管理
  TYPE EMP_RECORD_TYPE IS RECORD(
    NAME   EMP.ENAME%TYPE,
    SALARY EMP.SAL%TYPE,
    TITLE  EMP.JOB%TYPE);
  --定义了一个sp_record变量,这个变量的类型是emp_record_type
  SP_RECORD EMP_RECORD_TYPE;
BEGIN
  SELECT ENAME, SAL, JOB INTO SP_RECORD FROM EMP WHERE EMPNO = 7788;
  DBMS_OUTPUT.PUT_LINE('员工名:' || SP_RECORD.NAME || '工资:' || SP_RECORD.SALARY);
END;
/

pl/sql表:类似数组

方法一(推荐):
set serveroutput on; --打开输出选项
DECLARE
  --定义了一个pl/sql表类型sp_table_type,该类型是用于存放EMP.ENAME%TYPE
  --INDEX BY VARCHAR2(20)表示下标是字符串
  TYPE SP_TABLE_TYPE IS TABLE OF EMP.ENAME%TYPE INDEX BY VARCHAR2(20);
  --定义了一个sp_table变量,这个变量的类型是sp_table_type
  SP_TABLE SP_TABLE_TYPE;
BEGIN
  SELECT ENAME, sal INTO SP_TABLE('ename'), SP_TABLE('sal') FROM EMP WHERE EMPNO = 7788;
  DBMS_OUTPUT.PUT_LINE('员工名:' || SP_TABLE('ename')||'工资:'||SP_TABLE('sal'));
END;
/

方法二:
set serveroutput on; --打开输出选项
DECLARE
  --定义了一个pl/sql 表类型sp_table_type,该类型是用于存放EMP.ENAME%TYPE
  --index by binary_integer表示下标是整数
  TYPE SP_TABLE_TYPE IS TABLE OF EMP.ENAME%TYPE INDEX BY BINARY_INTEGER; --注意binary_integer如果换为integer就会报错,知道的朋友欢迎告诉我下
  --定义了一个sp_table变量,这个变量的类型是sp_table_type
  SP_TABLE SP_TABLE_TYPE;
BEGIN
  SELECT ENAME,sal INTO SP_TABLE(-1),SP_TABLE(-2) FROM EMP WHERE EMPNO = 7788;
  DBMS_OUTPUT.PUT_LINE('员工名:' || SP_TABLE(-1)||'工资:'||SP_TABLE(-2));
END;
/

3)、参照类型(reference)

参照变量——ref cursor游标变量:

SET serveroutput ON;
DECLARE
  --定义游标
  TYPE sp_emp_cursor IS REF CURSOR;
  --定义一个游标变量
  sp sp_emp_cursor;
  --定义变量
  v_ename emp.ename%TYPE;
  v_sal emp.sal%TYPE;
BEGIN
  OPEN sp FOR SELECT e.ename, e.sal FROM emp e WHERE e.deptno=10;
  --方法一 loop循环
  /*
  LOOP
  FETCH sp INTO v_ename, v_sal;
  EXIT WHEN sp%NOTFOUND;
  DBMS_OUTPUT.PUT_LINE('名字:' || V_ENAME || ' 工资:' || V_SAL);
  END LOOP;*/
  --方法二 while循环
  /*
  WHILE 1=1 LOOP
    FETCH sp INTO v_ename, v_sal;
    EXIT WHEN sp%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE('名字:' || V_ENAME || ' 工资:' || V_SAL);
  END LOOP;*/
  --方法三 for循环
  FOR cur IN (SELECT e.ename, e.sal FROM emp e WHERE e.deptno=10) LOOP
    DBMS_OUTPUT.PUT_LINE('名字:' || cur.ename || ' 工资:' || cur.sal);
  END LOOP;
END;
/
4)、lob(large object)

7:pl/sql控制结构

条件分支语句: if—then,if–then–else,if–then–else if–then

1)、简单的条件判断if–then
问题:编写一个过程,可以输入一个雇员名,如果该雇员的工资低于2000,就给该员工工资增加10%。

SET serveroutput ON;
CREATE OR REPLACE PROCEDURE SP_PRO6(SPNAME VARCHAR2) IS
  --定义
  V_SAL EMP.SAL%TYPE;
BEGIN
  --执行
  SELECT SAL INTO V_SAL FROM EMP WHERE ENAME = SPNAME;
  --判断
  IF V_SAL < 2000 THEN
    UPDATE EMP SET SAL = SAL + SAL * 0.1 WHERE ENAME = SPNAME;
    COMMIT;
  END IF;
END;
/

--调用存储过程
exec SP_PRO6('ALLEN');

2)、二重条件分支 if–then–else
问题:编写一个过程,可以输入一个雇员名,如果该雇员的补助不是0就在原来的基础上增加100;如果补助为0就把补助设为200;

CREATE OR REPLACE PROCEDURE SP_PRO6(SPNAME VARCHAR2) IS
  --定义
  V_COMM EMP.COMM%TYPE;
BEGIN
  --执行
  SELECT COMM INTO V_COMM FROM EMP WHERE ENAME = SPNAME;
  --判断
  IF V_COMM <> 0 THEN
    UPDATE EMP SET COMM = COMM + 100 WHERE ENAME = SPNAME;
  ELSE
    UPDATE EMP SET COMM = COMM + 200 WHERE ENAME = SPNAME;
  END IF;
  COMMIT;
END;
/

--调用存储过程
exec SP_PRO6('ALLEN');

3)、多重条件分支 if–then–ELSIF–then
问题:编写一个过程,可以输入一个雇员编号,如果该雇员的职位是PRESIDENT就给他的工资增加1000,如果该雇员的职位是MANAGER 就给他的工资增加500,其它职位的雇员工资增加200。

CREATE OR REPLACE PROCEDURE SP_PRO6(SPNO NUMBER) IS
  --定义
  V_JOB EMP.JOB%TYPE;
BEGIN
  --执行
  SELECT JOB INTO V_JOB FROM EMP WHERE EMPNO = SPNO;
  IF V_JOB = 'PRESIDENT' THEN
    UPDATE EMP SET SAL = SAL + 1000 WHERE EMPNO = SPNO;
  ELSIF V_JOB = 'MANAGER' THEN
    UPDATE EMP SET SAL = SAL + 500 WHERE EMPNO = SPNO;
  ELSE
    UPDATE EMP SET SAL = SAL + 200 WHERE EMPNO = SPNO;
  END IF;
  COMMIT;
END;
/
--调用存储过程
exec SP_PRO6(7499);

循环语句-loop:

CREATE OR REPLACE PROCEDURE SP_PRO6(SPNAME VARCHAR2) IS
  --定义 :=表示赋值
  V_NUM NUMBER := 1;
BEGIN
  LOOP
    INSERT INTO USERS VALUES (V_NUM, SPNAME);
    --判断是否要退出循环
    EXIT WHEN V_NUM = 10;
    --自增
    V_NUM := V_NUM + 1;
  END LOOP;
  COMMIT;
END;
/

--调用存储过程
EXEC SP_PRO6('ALLEN');

循环语句-while循环:

CREATE OR REPLACE PROCEDURE SP_PRO6(SPNAME VARCHAR2) IS
  --定义 :=表示赋值
  V_NUM NUMBER := 11;
BEGIN
  WHILE V_NUM <= 20 LOOP
    --执行
    INSERT INTO USERS VALUES (V_NUM, SPNAME);
    V_NUM := V_NUM + 1;
  END LOOP;
  COMMIT;
END;
/

--调用存储过程
EXEC SP_PRO6('ALLEN');

--调用存储过程
EXEC SP_PRO6('ALLEN');

循环语句-for循环:

CREATE OR REPLACE PROCEDURE SP_PRO6 IS--注意如果无参记得不要加()
BEGIN
  FOR I IN REVERSE 1 .. 10 LOOP --REVERSE反转函数,表示I从10到1递减,去掉REVERSE表示I从1到10递增
    INSERT INTO USERS VALUES (I, 'shunping');
  END LOOP;
END;
/

--调用存储过程
EXEC SP_PRO6;

顺序控制语句-goto、null:

set serveroutput on;
DECLARE
  I INT := 1;
BEGIN
  LOOP
    DBMS_OUTPUT.PUT_LINE('输出i=' || I);
    IF I = 1 THEN
      GOTO END_LOOP;
    END IF;
    I := I + 1;
  END LOOP;
  <<END_LOOP>>
  DBMS_OUTPUT.PUT_LINE('循环结束');
END;
/
SET serveroutput ON;
DECLARE
  V_SAL   EMP.SAL%TYPE;
  V_ENAME EMP.ENAME%TYPE;
BEGIN
  SELECT ENAME, SAL INTO V_ENAME, V_SAL FROM EMP WHERE EMPNO = &NO;
  IF V_SAL < 3000 THEN
    UPDATE EMP SET COMM = SAL * 0.1 WHERE ENAME = V_ENAME;
    dbms_output.put_line('1111');
  ELSE
    NULL;
    dbms_output.put_line('2222');--不会被执行
  END IF;
END;
/

8:pl/sql分页

一、无返回值的存储过程

古人云:欲速则不达,为了让大家伙比较容易接受分页过程编写,我还是从简单到复杂,循序渐进的给大家讲解。首先是掌握最简单的存储过程,无返回值的存储过程。 案例:现有一张表book,表结构如下:书号、书名、出版社。

CREATE TABLE book(
   ID NUMBER(4),
   book_name VARCHAR2(30),
   publishing VARCHAR2(30)
);

请写一个过程,可以向book表添加书,要求通过java程序调用该过程。

--注意:in->表示这是一个输入参数,默认为in --out->表示一个输出参数
CREATE OR REPLACE PROCEDURE ADD_BOOK(ID         IN NUMBER,
                                     NAME       IN VARCHAR2,
                                     PUBLISHING IN VARCHAR2) IS
BEGIN
  INSERT INTO BOOK VALUES (ID, NAME, PUBLISHING);
  COMMIT;
END;
/  

java程序调用该存储过程的代码

package junit.test;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;

/**
 * 调用一个无返回值的存储过程
 * 
 * @author jiqinlin
 *
 */
public class ProcedureTest {

    public static void main(String[] args) {

        try {
            // 1.加载驱动
            Class.forName("oracle.jdbc.driver.OracleDriver");
            // 2.得到连接
            Connection ct = DriverManager.getConnection(
                    "jdbc:oracle:thin:@127.0.0.1:1521:orcl", "scott", "oracle");
            // 3.创建CallableStatement
            CallableStatement cs = ct.prepareCall("call ADD_BOOK(?,?,?)");
            //给?赋值
            cs.setInt(1, 1);
            cs.setString(2, "java");
            cs.setString(3, "java出版社");
            // 4.执行
            cs.execute();
            //5、关闭
            cs.close();
            ct.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}     

二、有返回值的存储过程(非列表)
案例:编写一个存储过程,可以输入雇员的编号,返回该雇员的姓名。

--输入和输出的存储过程
CREATE OR REPLACE PROCEDURE SP_PROC(SPNO IN NUMBER, SPNAME OUT VARCHAR2) IS
BEGIN
  SELECT ENAME INTO SPNAME FROM EMP WHERE EMPNO = SPNO;
END;
/

java程序调用该存储过程的代码

package junit.test;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;

/**
 * 调用一个无返回值的存储过程
 * 
 * @author jiqinlin
 *
 */
public class ProcedureTest {

    public static void main(String[] args) {

        try {
            // 1.加载驱动
            Class.forName("oracle.jdbc.driver.OracleDriver");
            // 2.得到连接
            Connection ct = DriverManager.getConnection(
                    "jdbc:oracle:thin:@127.0.0.1:1521:orcl", "scott", "oracle");
            // 3.创建CallableStatement
            CallableStatement cs = ct.prepareCall("{call sp_proc(?,?)}");
            //给第一个?赋值
            cs.setInt(1,7788);
            //给第二个?赋值
            cs.registerOutParameter(2,oracle.jdbc.OracleTypes.VARCHAR);
            //4、执行
            cs.execute();
            //取出返回值,要注意?的顺序
            String name=cs.getString(2);
            System.out.println("编号7788的名字:"+name);
            //5、关闭
            cs.close();
            ct.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}  

案例扩张:编写一个过程,可以输入雇员的编号,返回该雇员的姓名、工资和岗位。

--输入和输出的存储过程
CREATE OR REPLACE PROCEDURE SP_PROC(SPNO   IN NUMBER,
                                    SPNAME OUT VARCHAR2,
                                    SPSAL  OUT NUMBER,
                                    SPJOB  OUT VARCHAR2) IS
BEGIN
  SELECT ENAME, SAL, JOB INTO SPNAME, SPSAL, SPJOB FROM EMP WHERE EMPNO = SPNO;
END;
/

java程序调用该存储过程的代码

package junit.test;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;

/**
 * 调用一个无返回值的存储过程
 * 
 * @author jiqinlin
 *
 */
public class ProcedureTest {

    public static void main(String[] args) {

        try {
            // 1.加载驱动
            Class.forName("oracle.jdbc.driver.OracleDriver");
            // 2.得到连接
            Connection ct = DriverManager.getConnection(
                    "jdbc:oracle:thin:@127.0.0.1:1521:orcl", "scott", "oracle");
            // 3.创建CallableStatement
            CallableStatement cs = ct.prepareCall("{call sp_proc(?,?,?,?)}");
            //给第一个?赋值
            cs.setInt(1,7788);
            //给第二个?赋值
            cs.registerOutParameter(2,oracle.jdbc.OracleTypes.VARCHAR);
            //给第三个?赋值
            cs.registerOutParameter(3,oracle.jdbc.OracleTypes.DOUBLE);
            //给第四个?赋值
            cs.registerOutParameter(4,oracle.jdbc.OracleTypes.VARCHAR);
            //4、执行
            cs.execute();
            //取出返回值,要注意?的顺序
            String name=cs.getString(2);
            double sal=cs.getDouble(3);
            String job=cs.getString(4);
            System.out.println("编号7788的名字:"+name+",职位:"+job+",薪水:"+sal+"");
            //5、关闭
            cs.close();
            ct.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

三、有返回值的存储过程(列表[结果集])
案例:编写一个存储过程,输入部门号,返回该部门所有雇员信息。
该题分析如下:由于oracle存储过程没有返回值,它的所有返回值都是通过out参数来替代的,列表同样也不例外,但由于是集合,所以不能用一般的参数,必须要用pagkage了。所以要分两部分:
1)、建立一个包,在该包中我们定义类型test_cursor,它是个游标。

CREATE OR REPLACE PACKAGE TESTPACKAGE AS
  TYPE TEST_CURSOR IS REF CURSOR;
END TESTPACKAGE;
/

2)、建立存储过程。

CREATE OR REPLACE PROCEDURE SP_PROC(SPNO     IN NUMBER,
                                    P_CURSOR OUT TESTPACKAGE.TEST_CURSOR) IS
BEGIN
  OPEN P_CURSOR FOR
    SELECT * FROM EMP WHERE DEPTNO = SPNO;
END SP_PROC;
/   

3)、如何在java 程序中调用该过程

package junit.test;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;

/**
 * 调用一个无返回值的存储过程
 * 
 * @author jiqinlin
 *
 */
public class ProcedureTest {

    public static void main(String[] args) {

        try {
            // 1.加载驱动
            Class.forName("oracle.jdbc.driver.OracleDriver");
            // 2.得到连接
            Connection ct = DriverManager.getConnection(
                    "jdbc:oracle:thin:@127.0.0.1:1521:orcl", "scott", "oracle");
            // 3.创建CallableStatement
            CallableStatement cs = ct.prepareCall("{call sp_proc(?,?)}");
            //给第一个?赋值
            cs.setInt(1,10);
            //给第二个?赋值
            cs.registerOutParameter(2,oracle.jdbc.OracleTypes.CURSOR);
            //4、执行
            cs.execute();
            //得到结果集
            ResultSet rs = (ResultSet) cs.getObject(2);
            while (rs.next()) {
                System.out.println(rs.getInt(1) + " " + rs.getString(2));
            }
            //5、关闭
            rs.close();
            cs.close();
            ct.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

四、编写分页过程
有了上面的基础,相信大家可以完成分页存储过程了。
要求,请大家编写一个存储过程,要求可以输入表名、每页显示记录数、当前页。返回总记录数,总页数,和返回的结果集。

--ROWNUM用法
SELECT o.*, ROWNUM RN FROM (SELECT * FROM EMP) o WHERE ROWNUM <= 10;
----oracle分页sql语句;在分页时,大家可以把下面的sql语句当做一个模板使用
SELECT *
FROM (SELECT o.*, ROWNUM RN FROM (SELECT * FROM EMP) o WHERE ROWNUM <= 10)
WHERE RN >= 6; 

1)、开发一个包
建立一个包,在该包中定义类型为test_cursor的游标。

--建立一个包
CREATE OR REPLACE PACKAGE TESTPACKAGE AS
  TYPE TEST_CURSOR IS REF CURSOR;
END TESTPACKAGE;
/

--开始编写分页的过程
CREATE OR REPLACE PROCEDURE FENYE(TABLENAME IN VARCHAR2, 
                                  PAGESIZE IN NUMBER, --每页显示记录数
                                  PAGENOW IN NUMBER, --页数
                                  MYROWS OUT NUMBER, --总记录数
                                  MYPAGECOUNT OUT NUMBER, --总页数
                                  P_CURSOR OUT TESTPACKAGE.TEST_CURSOR) IS --返回的记录集
                                 
--定义部分
--定义sql语句字符串
V_SQL VARCHAR2(1000);
--定义两个整数
V_BEGIN NUMBER := (PAGENOW - 1) * PAGESIZE + 1; 
V_END NUMBER := PAGENOW * PAGESIZE;
BEGIN
--执行部分
V_SQL := 'select * from (select t1.*, rownum rn from (select * from ' || TABLENAME || ') t1 where rownum<=' || V_END || ') where rn>=' || V_BEGIN;
--把游标和sql关联
OPEN P_CURSOR FOR V_SQL;
--计算myrows和myPageCount
--组织一个sql语句
V_SQL := 'select count(*) from ' || TABLENAME;
--执行sql,并把返回的值,赋给myrows;
EXECUTE ImMEDIATE V_SQL INTO MYROWS; --它解析并马上执行动态的SQL语句或非运行时创建的PL/SQL块.动态创建和执行SQL语句性能超前,
                                     --EXECUTE IMMEDIATE的目标在于减小企业费用并获得较高的性能,较之以前它相当容易编码.
                                     --尽管DBMS_SQL仍然可用,但是推荐使用EXECUTE IMMEDIATE,因为它获的收益在包之上。 
--计算myPageCount
--if myrows%Pagesize=0 then 这样写是错的
IF MOD(MYROWS, PAGESIZE) = 0 THEN 
  MYPAGECOUNT := MYROWS/PAGESIZE; 
ELSE 
  MYPAGECOUNT := MYROWS/PAGESIZE + 1;
END IF;
--关闭游标
--CLOSE P_CURSOR; --不要关闭,否则java调用该存储过程会报错
END;
/  

java调用分页代码

package junit.test;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;

/**
 * 调用一个无返回值的存储过程
 * 
 * @author jiqinlin
 * 
 */
public class ProcedureTest {

    public static void main(String[] args) {

        try {
            // 1.加载驱动
            Class.forName("oracle.jdbc.driver.OracleDriver");
            // 2.得到连接
            Connection ct = DriverManager.getConnection(
                    "jdbc:oracle:thin:@127.0.0.1:1521:orcl", "scott", "oracle");
            // 3.创建CallableStatement
            CallableStatement cs = ct.prepareCall("{call fenye(?,?,?,?,?,?)}");
            cs.setString(1, "emp"); //表名
            cs.setInt(2, 5); //每页显示记录数
            cs.setInt(3, 1);//页数
            // 注册总记录数
            cs.registerOutParameter(4, oracle.jdbc.OracleTypes.INTEGER); //总记录数
            // 注册总页数
            cs.registerOutParameter(5, oracle.jdbc.OracleTypes.INTEGER); //总页数
            // 注册返回的结果集
            cs.registerOutParameter(6, oracle.jdbc.OracleTypes.CURSOR); //返回的记录集
            // 4、执行
            cs.execute();
            // 得到结果集
            // 取出总记录数 /这里要注意,getInt(4)中4,是由该参数的位置决定的
            int rowNum = cs.getInt(4);

            int pageCount = cs.getInt(5);
            ResultSet rs = (ResultSet) cs.getObject(6);
            // 显示一下,看看对不对
            System.out.println("rowNum=" + rowNum);
            System.out.println("总页数=" + pageCount);

            while (rs.next()) {
                System.out.println("编号:" + rs.getInt(1) + 
                        " 名字:" + rs.getString(2) + 
                        " 工资:" + rs.getFloat(6));
            }
            // 5、关闭
            //rs.close();
            cs.close();
            ct.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

十五:oracle例外

1)、预定义例外用于处理常见的oracle错误。
2)、非预定义例外用于处理预定义例外不能处理的例外。
3)、自定义例外用于处理与oracle错误无关的其它情况。

1:处理预定义例外

case_not_found预定义例外 :

SET SERVEROUTPUT ON;
CREATE OR REPLACE PROCEDURE SP_PRO6(SPNO NUMBER) IS
  V_SAL EMP.SAL%TYPE;
BEGIN
  SELECT SAL INTO V_SAL FROM EMP WHERE EMPNO = SPNO;
  CASE
    WHEN V_SAL < 1000 THEN
      UPDATE EMP SET SAL = SAL + 100 WHERE EMPNO = SPNO;
    WHEN V_SAL < 2000 THEN
      UPDATE EMP SET SAL = SAL + 200 WHERE EMPNO = SPNO;
  END CASE;
EXCEPTION
  WHEN CASE_NOT_FOUND THEN
    DBMS_OUTPUT.PUT_LINE('case语句没有与' || V_SAL || '相匹配的条件');
END;
/

--调用存储过程
SQL> EXEC SP_PRO6(7369);
case语句没有与4444相匹配的条件

cursor_already_open预定义例外 :

DECLARE
  CURSOR EMP_CURSOR IS
    SELECT ENAME, SAL FROM EMP;
BEGIN
  OPEN EMP_CURSOR; --声明时游标已打开,所以没必要再次打开
  FOR EMP_RECORD1 IN EMP_CURSOR LOOP
    DBMS_OUTPUT.PUT_LINE(EMP_RECORD1.ENAME);
  END LOOP;
EXCEPTION
  WHEN CURSOR_ALREADY_OPEN THEN
    DBMS_OUTPUT.PUT_LINE('游标已经打开');
END;
/

dup_val_on_index预定义例外 :

BEGIN
  INSERT INTO DEPT VALUES (10, '公关部', '北京');
EXCEPTION
  WHEN DUP_VAL_ON_INDEX THEN
    DBMS_OUTPUT.PUT_LINE('在deptno列上不能出现重复值');
END;
/

invalid_cursorn预定义例外 :

DECLARE
  CURSOR EMP_CURSOR IS
    SELECT ENAME, SAL FROM EMP;
  EMP_RECORD EMP_CURSOR%ROWTYPE;
BEGIN
  --open emp_cursor; --打开游标
  FETCH EMP_CURSOR INTO EMP_RECORD;
  DBMS_OUTPUT.PUT_LINE(EMP_RECORD.ENAME);
  CLOSE EMP_CURSOR;
EXCEPTION
  WHEN INVALID_CURSOR THEN
    DBMS_OUTPUT.PUT_LINE('请检测游标是否打开');
END;
/

invalid_number预定义例外 :

SET SERVEROUTPUT ON;
BEGIN
  UPDATE EMP SET SAL = SAL + 'AAA';
EXCEPTION
  WHEN INVALID_NUMBER THEN
    DBMS_OUTPUT.PUT_LINE('输入的数字不正确');
END;
/

no_data_found预定义例外 :

SET serveroutput ON;
DECLARE
  V_SAL EMP.SAL%TYPE;
BEGIN
  SELECT SAL INTO V_SAL FROM EMP WHERE ENAME = 'ljq';
EXCEPTION
  WHEN NO_DATA_FOUND THEN
    DBMS_OUTPUT.PUT_LINE('不存在该员工');
END;
/

too_many_rows预定义例外 :

DECLARE
  V_ENAME EMP.ENAME%TYPE;
BEGIN
  SELECT ENAME INTO V_ENAME FROM EMP;
EXCEPTION
  WHEN TOO_MANY_ROWS THEN
    DBMS_OUTPUT.PUT_LINE('返回了多行');
END;
/

zero_divide预定义例外 : 当执行2/0语句时,则会触发该例外

value_error预定义例外 : 当在执行赋值操作时,如果变量的长度不足以容纳实际数据,则会触发该例外value_error

login_denied : 当用户非法登录时,会触发该例外

not_logged_on:如果用户没有登录就执行dml操作,就会触发该例外
storage_error:如果超过了内存空间或是内存被损坏,就触发该例外
timeout_on_resource:如果oracle在等待资源时,出现了超时就触发该例外

2:非预定义例外:非预定义例外用于处理与预定义例外无关的oracle错误。使用预定义例外只能处理21个oracle 错误,而当使用pl/sql开发应用程序时,可能会遇到其它的一些oracle错误。比如在pl/sql块中执行dml语句时,违反了约束规定等等。

3:处理预定义例外: 预定义例外和自定义例外都是与oracle错误相关的,并且出现的oracle 错误会隐含的触发相应的例外;而自定义例外与oracle 错误没有任何关联,它是由开发人员为特定情况所定义的例外.

CREATE OR REPLACE PROCEDURE EX_TEST(SPNO NUMBER) IS
BEGIN
  UPDATE EMP SET SAL = SAL + 1000 WHERE EMPNO = SPNO;
END;
/

--调用存储过程,
EXEC EX_TEST(56);
CREATE OR REPLACE PROCEDURE EX_TEST(SPNO NUMBER) IS
--定义一个例外
MYEX EXCEPTION;
BEGIN
--更新用户sal
UPDATE EMP SET SAL = SAL + 1000 WHERE EMPNO = SPNO;
--sql%notfound 这是表示没有update
--raise myex;触发myex
IF SQL%NOTFOUND THEN RAISE MYEX;
END IF;
EXCEPTION
WHEN MYEX THEN DBMS_OUTPUT.PUT_LINE('没有更新任何用户');
END;
/

转载于:https://my.oschina.net/zhuqingbo0501/blog/1815912

你可能感兴趣的:(数据库,java,人工智能)