oracle

关注公众号 jb51net

关闭
首页 > 数据库 > oracle > Oracle存储过程

一文详解Oracle存储过程

作者:你才是臭弟弟

Oracle存储过程是Oracle数据库中的一种数据处理对象,它可以在数据库中定义一组预定义的SQL语句,用于完成特定的数据库操作,本文将通过代码示例详细介绍了Oracle存储过程,需要的朋友可以参考下

简介

Oracle 存储过程是 Oracle 数据库中的一种数据处理对象,它可以在数据库中定义一组预定义的 SQL 语句,用于完成特定的数据库操作。存储过程可以被授权的用户调用,并且可以执行多个语句,这些语句可以被视为一个单独的操作,也可以被视为一系列的操作。

使用存储过程可以大大提高数据库的性能和安全性。存储过程可以减少网络流量和请求,同时也可以减少与应用程序之间的接口调用,从而提高了数据库的性能和可靠性。

1、基本语法 

1.1 新建测试窗口

1.2 程序结构

-- Created on 2023/5/10 by 肖 
declare 
  --声明变量 游标
 
begin
  -- 执行语句
  
  -- 异常处理
end;

1.3打印输出  

begin
  
  --打印 hello mr.xiao
 Dbms_Output.put_line('hello mr.xiao');
 
end;

执行结果

1.4 变量

变量分两大类如:

声明变量的方式如:

1.4.1 普通变量

变量赋值的方式有两种如:

  -- 打印个人信息,包括: 姓名、薪水、地址
DECLARE
  -- 姓名
  V_NAME VARCHAR2(30) := '你才是臭弟弟'; -- 声明变量直接赋值
  --薪水
  V_SAL NUMBER;
  --地址
  V_ADDR VARCHAR2(200);
 
BEGIN
 
  --在程序中直接赋值
  V_SAL := 1800; --工资每月1800 每天笑哈哈
 
  --语句赋值
  SELECT 'CSDN你才是臭弟弟' INTO V_ADDR FROM DUAL; --不会有人不知道DUAL吧,DUAL 是一个用于描述 Oracle 数据库中的虚拟表的关键字
 
  --打印变量  注意 || 是拼接
  DBMS_OUTPUT.PUT_LINE('姓名:' || V_NAME || ',薪水:' || V_SAL || ',地址:' ||V_ADDR);
 
END;

执行结果:

1.4.2 引用型变量

  -- 查询emp表中1001号员工的个人信息,打印姓名和薪水
DECLARE
  -- 姓名
  V_NAME EMP.ENAME%TYPE; -- 声明变量直接赋值
  --薪水
  V_SAL  EMP.ESALARY%TYPE;
 
BEGIN
  --查询表中的姓名和薪水并赋值给变量
  --注意查询的字段和赋值的变量的顺序、个数、类型要一致
  SELECT ENAME, ESALARY INTO V_NAME, V_SAL FROM EMP WHERE EMPLOYEEID = 1001;
 
  --打印变量
  DBMS_OUTPUT.PUT_LINE('姓名:' || V_NAME || ',薪水:' || V_SAL);
 
END;

执行结果:

推荐大家使用引用型变量区别:

总结:

使用普通变量定义方式,需要知道表中列的类型,而使用引用类型,不需要考虑列的类型,使用%TYPE是非常好的编程风格,因为引用型变量更加灵活。

1.4.3 记录型变量  

  -- 查询emp表中1001号员工的个人信息,打印姓名和薪水
DECLARE
  -- 记录型变量接受一行
  V_EMP EMP%ROWTYPE;
 
BEGIN
  --记录型变量默认接受表中的一行数据,不能指定字段。
  SELECT * INTO V_EMP FROM EMP WHERE EMPLOYEEID = 1001;
 
  --打印变量,通过变量名.属性的方式获取变量中的值
  DBMS_OUTPUT.PUT_LINE('姓名:' || V_EMP.ENAME || ',薪水:' || V_EMP.ESALARY);
 
END;

总结:

注意错误的使用案例如下:

我把 * 换成 单个字段执行报错,因为上面的变量定义的是一行,而现在只给一个是不行的。

现在这条sql 为什么报错,因为现在是全表查询 返回的行数超出了 一个变量只能接收一行,这就和JAVA 类似了,应该用集合去装才可以 ,装进集合在取出来  是不是就跟JAVA 中循环取值差不多。

1.5 流程控制

1.5.1 条件分支

BEGIN
 
  IF 条件 THEN
      执行语句
  END IF;
 
END;
BEGIN
 
 IF 条件 THEN
    执行语句
 ELSE
    执行语句
 END IF;
 
END;
BEGIN
 
  IF 条件1 THEN 执行1
    
   ELSIF 条件2 THEN 执行2
  
   ELSE 执行3
    
  END IF; 
  
END;

案例:

  --判断emp表中记录是否超过20条,10-20之间,或者10条以下
DECLARE
  --声明变量接受emp表中的记录数
  V_COUNT NUMBER;
 
BEGIN
 
  --查询emp表中的记录数赋值给变量
 
  SELECT COUNT(1) INTO V_COUNT FROM EMP;
 
  --判断打印
 
  IF V_COUNT > 20 THEN
    DBMS_OUTPUT.PUT_LINE('EMP表中的记录数超过了20条为:' || V_COUNT || '条。');
 
  ELSIF V_COUNT >= 10 THEN
    DBMS_OUTPUT.PUT_LINE('EMP表中的记录数在10~20条之间为:' || V_COUNT || '条。');
 
  ELSE
    DBMS_OUTPUT.PUT_LINE('EMP表中的记录数在10条以下为:' || V_COUNT || '条。');
 
  END IF;
 
END;

执行结果:

1.5.2 循环

BEGIN
  LOOP
  	EXIT WHEN 退出循环条件  
  END LOOP;
END;

Loop语法 案例:

  --循环打印 1-5
DECLARE
  --声明循环变量并赋初值
  V_NUM NUMBER := 1;
 
BEGIN
 
  LOOP
  
    EXIT WHEN V_NUM > 5;
    
    DBMS_OUTPUT.PUT_LINE(V_NUM);
  
    --循环变量自增
    V_NUM := V_NUM + 1;
  
  END LOOP;
 
END;
while(判断循环的条件) loop
	循环的语句;
END loop;

While语法 案例:

DECLARE
    --声明循环变量
    V_NUM NUMBER;
BEGIN
    -- 必须给一个初始值
    V_NUM := 1;
    WHILE(V_NUM < 10) LOOP
        DBMS_OUTPUT.put_line('值为: ' || V_NUM);
        V_NUM := V_NUM + 1;
    END LOOP;
END;
 
--此循环会先判断再执行语句
FOR 变量名 in 变量的初始值..结束值 lOOP
	循环语句;
END loop;

FOR循环语法 案例:

    --for循环打印 1-10
DECLARE
    --声明循环变量并赋初值
    V_NUM NUMBER ;
BEGIN
	  --此语句会自动将1到10赋值给V_NUM
    FOR V_NUM in 1..10 loop
        DBMS_OUTPUT.put_line('值为: ' || V_NUM);
    END LOOP;
END;
 

2、游标

2.1 游标说明

游标声明:

CURSOR  游标名[(参数列表)]    IS 查询语句;

游标的打开:

OPEN 游标名;

游标的取值:

FETCH 游标名 INTO 变量列表;

游标的关闭:

CLOSE 游标名;

 注意: 游标名自身是可以带参数的,如果有参数、参数会带入到查询语句中进行查询,游标本质 就是 一个 is 查询语句,也就是说查询结果被放置到游标中。

2.3 游标属性

游标的属性

属性说明
 %FOUND变量最后从游标中获取记录的时候,在结果集中找到了记录。
%NOTFOUND变量最后从游标中获取记录的时候,在结果集中没有找到记录。
%ROWCOUNT当前时刻已经从游标中获取的记录数量。
%ISOPEN 是否打开。
%ROW游标指向的行数。 
%COLUMN游标指向的列数。
%ATTEMPTS尝试获取记录的次数。
%ERROR 发生错误的次数。
%FETCH_STATUS FETCH语句的执行状态,包括成功、失败和出错标志。
%SIZE当前游标指向的记录大小。
%LINE_NUMBER当前行号。
%ERROR_STRING错误信息字符串。
%PROCID当前执行的SQL语句的ID

2.4 无参数游标

--使用游标查询emp表中所有员工的姓名和工资,依次打印结果集。
DECLARE
  --声明游标
  CURSOR C_EMP IS
    SELECT ENAME, ESALARY FROM EMP;
 
  --声明变量用来接受游标中的元素
  V_ENAME EMP.ENAME%TYPE;
 
  V_SAL EMP.ESALARY%TYPE;
 
BEGIN
 
  --打开游标
  OPEN C_EMP;
 
  --遍历游标中的值
  LOOP
  
    --通过FETCH语句获取游标中的值并赋值给变量
    FETCH C_EMP
      INTO V_ENAME, V_SAL;
  
    --通过%NOTFOUND判断是否有值,有值打印,没有则退出循环
    EXIT WHEN C_EMP%NOTFOUND;
  
    DBMS_OUTPUT.PUT_LINE('姓名:' || V_ENAME || ',薪水:' || V_SAL);
  
  END LOOP;
 
  --关闭游标
  CLOSE C_EMP;
 
END;

执行结果:

2.5 带参数的游标

 --使用游标查询并打印某部门的员工的姓名和薪资,部门编号为运行时手动输入。
DECLARE
  --声明游标传递参数
  CURSOR C_EMP(V_EMPLOID EMP.EMPLOYEEID%TYPE) IS
    SELECT ENAME, ESALARY FROM EMP WHERE EMPLOYEEID = V_EMPLOID; 
 
  --声明变量用来接受游标中的元素
  V_ENAME EMP.ENAME%TYPE;
 
  V_SAL EMP.ESALARY%TYPE;
 
BEGIN
 
  --打开游标并传递参数
  OPEN C_EMP(1001);
 
  --遍历游标中的值
  LOOP
 
  --通过FETCH语句获取游标中的值并赋值给变量
    FETCH C_EMP
      INTO V_ENAME, V_SAL;
 
 
       --通过%NOTFOUND判断是否有值,有值打印,没有则退出循环
       EXIT WHEN C_EMP%NOTFOUND;
       
       
 
    DBMS_OUTPUT.PUT_LINE('姓名:' || V_ENAME || ',薪水:' || V_SAL);
 
 
  END LOOP;
 
  --关闭游标
  CLOSE C_EMP;
 
END;

执行结果:

注意:%NOTFOUND属性默认值为FLASE,所以在循环中要注意判断条件的位置.如果先判断在FETCH会导致最后一条记录的值被打印两次(多循环一次默认);

错误反例演示:

反例执行结果:

原因:  %NOTFOUND 默认值是 false,false意味着游标里面默认是有值,到底有值还是没值 需要fetch 好之后才知道有没有值,%NOTFOUND 默认做了一个有值的假设 , 看下面代码:

LOOP


       --通过%NOTFOUND判断是否有值,有值打印,没有则退出循环
       EXIT WHEN C_EMP%NOTFOUND;


     --通过FETCH语句获取游标中的值并赋值给变量
     FETCH C_EMP
        INTO V_ENAME, V_SAL;
       

      DBMS_OUTPUT.PUT_LINE('姓名:' || V_ENAME || ',薪水:' || V_SAL);


END LOOP;

EXIT WHEN C_EMP%NOTFOUND; 判断有值打印,出去之后 又带着有值的进入到循环中 EXIT WHEN C_EMP%NOTFOUND; 判断没值打印, 所以这次打印的是上次值的。注意存放的位置。

3、存储过程

3.1 概念 

3.2 语法

参数的类型分为:

CREATE OR REPLACE PROCEDURE 过程名称[(参数列表)] IS
BEGIN
 
END 过程名称;

3.3 无参存储

3.3.1 创建存储

--通过调用存储过程打印hello 臭弟弟
CREATE OR REPLACE PROCEDURE P_XIAO IS
--声明变量
BEGIN
 
  DBMS_OUTPUT.PUT_LINE('hello 臭弟弟');
 
END P_XIAO ;

1、is和as都可以用。

2、存储过程中没有declare关键字,declare用在语句块中。也就是说匿名程序才需要, 存储过程没有可以直接带上方--声明变量。

3.3.2 调用存储过程 

begin
  --输入调用存储过程的名称
  P_XIAO;
  
end;

查看结果:

3.4 带输入参数的存储过程 IN

说明:

3.4.1 创建带参数存储过程

执行结果:

--查询并打印某个员工(如:编号1001)的姓名和薪水, 要调用存储过程的时候传入员工编号,自动控制台打印。
CREATE OR REPLACE PROCEDURE P_xiao_jian(IN_EMPLOYEEID IN EMP.EMPLOYEEID%TYPE) as
  --声明变量接受查询结果
  V_ENAME EMP.ENAME%TYPE;
  V_SAL   EMP.ESALARY%TYPE;
 
BEGIN
 
  --根据用户传递的员工号查询姓名和薪水   
  --注意:参数要与定义的参数的顺序和类型一致 如: ENAME  INTO  V_ENAME 
  SELECT ENAME, ESALARY INTO V_ENAME, V_SAL FROM EMP WHERE EMPLOYEEID = IN_EMPLOYEEID;
 
  --打印结果
  DBMS_OUTPUT.PUT_LINE('姓名:' || V_ENAME || ',薪水:' || V_SAL);
 
END P_xiao_jian;

3.4.2 调用带参数存储过程

-- Created on 2023/5/13 by 肖 
declare 
  -- Local variables here
  i integer;
begin
  -- Test statements here
  P_XIAO_JIAN(1001);--直接赋值  做一个值的传递
  
end;
-- Created on 2023/5/13 by 肖 
declare 
  -- Local variables here
   V_PARAM number :=1001; --声明变量  赋值
begin
  -- Test statements here
  P_XIAO_JIAN(V_PARAM);
  
end;

查看执行结果:

3.5 带输入输出参数的存储过程  OUT

说明:

3.5.1 创建带输入输出参数的存储过程

--输入员工号查询某个员工(如:编号1001)信息,要求将薪水作为返回值输出,给调用的程序使用。
CREATE OR REPLACE PROCEDURE P_XIAO_JIAN(IN_EMPLOYEEID IN EMP.EMPLOYEEID%TYPE,OUT_ESALARY OUT EMP.ESALARY%TYPE) as
 
BEGIN
 
  --查询 ESALARY into 给 OUT_ESALARY 输出变量
  SELECT ESALARY INTO OUT_ESALARY FROM EMP WHERE EMPLOYEEID = IN_EMPLOYEEID;
 
 
END P_XIAO_JIAN;

3.5.2 调用带输入输出参数存储过程

DECLARE
  --声明一个变量接受存储过程的输出参数
  V_ESALARY EMP.ESALARY%TYPE;
 
BEGIN
 
  P_XIAO_JIAN(1001, V_ESALARY); --注意参数的顺序
 
  DBMS_OUTPUT.PUT_LINE('工资:'||V_ESALARY);
 
END; 

执行结果:

3.6 带输入输出参数的存储过程 IN OUT

3.6.1 创建带输入输出参数的存储过程

--输入员工号查询某个员工(如:编号1001)信息,要求将薪水作为返回值输出,给调用的程序使用。
CREATE OR REPLACE PROCEDURE P_xiao_jian(IN_EMPLOYEEID IN  EMP.EMPLOYEEID%TYPE,OUT_ESALARY  OUT EMP.ESALARY%TYPE,IN_OUT_PARAM in out number) as
 
BEGIN
 
 
  --查询 ESALARY into 给 OUT_ESALARY 输出变量
  SELECT ESALARY INTO OUT_ESALARY FROM EMP WHERE EMPLOYEEID = IN_EMPLOYEEID;
  --打印被传入的值
  dbms_output.put_line('我是被传入的值'||IN_OUT_PARAM);
  --IN_OUT_PARAM赋值默认值为10
  IN_OUT_PARAM:=10;
 
END P_xiao_jian;
 
 

3.6.2 调用带输入输出参数存储过程

-- Created on 2023/5/16 by 肖 
declare 
  -- Local variables here
  
  V_ESALARY EMP.ESALARY%TYPE;
 
  V_IN_OUT_PARAM number:=6;--传入的值
  
 
begin
  -- Test statements here
  P_xiao_jian(1001,V_ESALARY,V_IN_OUT_PARAM);
  
  DBMS_OUTPUT.PUT_LINE('薪水:'||V_ESALARY||'原始默认值: '||V_IN_OUT_PARAM);
  
 
end;
 
 

执行结果:

以上就是一文详解Oracle存储过程的详细内容,更多关于Oracle存储过程的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文