Oracle存储过程和自定义函数详细讲解

数据库 专栏收录该内容
16 篇文章 0 订阅

存储过程和存储函数


存储在数据库中供所有用户程序调用的子程序叫做存储过程存储函数

区别:是否可以通过return返回函数值。存储函数可以通过return返回函数值,而存储过程不可以。
相同点:完成特定功能的程序。

由于通过out参数,存储过程也可以返回函数值,所以存储过程和存储函数已经没有太大的区别了。而存储函数仍然存在,是由于oracle不断升级,需要实现向下兼容,所以存储函数就一直存留着。

存储过程


创建和使用存储过程

create procedure命令建立存储过程和存储函数。

语法:

create [or replace] procedure 过程名(参数列表)
as
PLSQL子程序体;

注意事项:

  • 存储过程或者存储函数,只能创建或者替换;
  • 参数可以带也可以不带;
  • as相当于PLSQL语句中的declare,用来声明变量,游标等,但是不可以省略。
入门案例

不带参数的存储过程:不用带括号

--第一个存储过程:打印HelloWorld
/*
调用存储过程的两种方式:
1、exec sayHelloWorld();
2、begin
       sayHelloWorld();
       sayHelloWorld();
   end;
   /
*/
create or replace procedure sayHelloWorld
as
--说明部分
begin
   dbms_output.put_line('HelloWorld');
end;

在PL/SQL Developer中执行存储过程:
这里写图片描述

--如果想要在命令窗口显示(即打印出来),要先
SQL> set serveroutput on
--第一种调用方式
SQL> exec sayHelloWorld()

HelloWorld

PL/SQL procedure successfully completed

SQL> 
--第二种调用方式
SQL> begin
  2      sayHelloWorld();
  3      sayHelloWorld();
  4  end;
  5  /

HelloWorld
HelloWorld

PL/SQL procedure successfully completed

带参数的存储过程

给指定的员工涨100元工资,并且打印涨前和涨后的薪水。

使用的表和数据:

create table EMP
(
  empno VARCHAR2(16) not null,
  ename VARCHAR2(16) not null,
  sal   NUMBER,
  comm  NUMBER,
  job   VARCHAR2(16),
  deptno NUMBER
);

insert into EMP (empno, ename, sal, comm, job, deptno) values ('7839', 'xiaoming', 1200, 500,  'Actor', 12);
insert into EMP (empno, ename, sal, job, deptno) values ('7566', 'xiaowang', 800, 'Lawyer', 12);
commit;

创建存储过程:

--创建一个带参数的存储过程:
--给指定的员工涨100块钱的工资,并且打印涨前和涨后的薪水
/*
如何调用:
begin
    addSal(7839);
    addSal(7566);
end;
/
*/
create or replace procedure addSal(pempno in emp.empno%type)
as
  --定义变量
  pename emp.ename%type;
  beforesal emp.sal%type;
  aftersal emp.sal%type;
begin
  --得到员工涨前的薪水
  select ename,sal into pename,beforesal from emp where empno=pempno;
  --给该员工涨100
  aftersal:=beforesal+100;
  update emp set sal=aftersal where empno=pempno; 

  --需不需要commit?
  --注意:一般不在存储过程或者存储函数中,commit和rollback

  --打印
  dbms_output.put_line('姓名: '||pename||' 涨前工资:'||beforesal||'涨后工资:'||aftersal);
end;
/

执行结果:

SQL> set serveroutput on
SQL> 
SQL> begin
  2      addSal(7839);
  3      addSal(7566);
  4  end;
  5  /

姓名: xiaoming 涨前工资:1200 涨后工资:1300
姓名: xiaowang 涨前工资:800 涨后工资:900

PL/SQL procedure successfully completed

注意:

  • 要说明,参数是输入参数(in)还是输出参数(out);
  • 为保证调用多个存储过程中处在同一个事务中,所以一般不在存储过程或者存储函数中,commit或rollback;

存储函数


存储函数
  • 函数(Function)为一命名的存储程序,可带参数,并返回一计算值;
  • 函数和过程的结构类似,但必须有一个return子句,用于返回函数值。
创建存储函数的语法
create [or replace] function 函数名(参数列表)
return 函数值类型
as
PLSQL子程序体;

注意事项:

  • 与存储过程注意事项类似,不同的是,必须有个返回值;
  • 参数列表可以有,也可以没有.当没有时,函数名后面不要带括号。

例子:查询某个员工的年收入。

--存储函数:查询某个员工的年收入
create or replace function queryempannal(pempno in number)
return number
as
  --定义变量保存员工的薪水和奖金
  psal emp.sal%type;
  pcomm emp.comm%type;
begin
  --得到该员工的月薪和奖金
  select sal,comm into psal,pcomm from emp where empno=pempno;
  --直接返回年收入
  return psal*12+nvl(pcomm,0);
end;

in和out参数


概述
  • 一般来讲,存储过程和存储函数的区别在于存储函数可以有一个返回值,而存储过程没有返回值;
  • 过程和函数都可以通过out指定一个或多个输出参数。我们可以利用out参数,在过程和函数中实现返回多个值;
    • 存储过程和存储函数都可以有out参数;
    • 存储过程和存储函数都可以有多个out参数;
    • 存储过程可以通过out参数来实现返回值。
  • 什么时候用存储过程/存储函数?
    • 原则:如果只有一个返回值,用存储函数;否则,就用存储过程。
--out参数:查询某个员工姓名、月薪和职位
/*
思考:
1、查询某个员工的所有信息---->out参数太多?
2、查询某个部门中所有员工的所有信息----->out中返回集合?
*/
create or replace procedure queryempinform(eno in number,
                                           pename out varchar2,
                                           psal out number,
                                           pjob out varchar2 )
as
begin
  --得到该员工的姓名、月薪和职位
  select ename,sal,job into pename,psal,pjob from emp where empno=eno;
end;

在应用程序中访问存储过程和存储函数


这里写图片描述

先看一下数据库工具类:

package test.oracle.utils;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class JDBCUtils {
    private static String driver = "oracle.jdbc.OracleDriver";
    private static String url = "jdbc:oracle:thin:@20.26.26.250:1521:ZJDQ_TEST";
    private static String user = "DDZX";
    private static String password = "ddzx_1Q#";

    // 注册数据库的驱动
    static {
        try {
            Class.forName(driver);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    // 获取数据库连接
    public static Connection getConnection() {
        try {
            return DriverManager.getConnection(url, user, password);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    // 释放数据库资源
    public static void release(Connection conn, Statement sta, ResultSet rst) {
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                conn = null;
            }
        }
        if (sta != null) {
            try {
                sta.close();
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                sta = null;
            }
        }
        if (rst != null) {
            try {
                rst.close();
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                rst = null;
            }
        }
    }
}
访问存储过程
package test.oracle.demo;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;

import org.junit.Test;

import oracle.jdbc.OracleTypes;
import test.oracle.utils.JDBCUtils;

public class TestProcedure {
    /*
    create or replace procedure queryempinform(eno in number,
                                        pename out varchar2,
                                        psal out number,
                                        pjob out varchar2 )
         as
         begin
           select ename,sal ,job into pename, psal,pjob from emp where empno=eno ;
         end;
         /
    */
    @Test
    public void testProcedure() {
        /*
         * {call <procedure-name>[(<arg1>,<arg2>, ...)]}
         */
        String sql = "{call queryempinform(?,?,?,?)}";
        Connection conn = null;
        CallableStatement sta = null;
        try {
            // 获取连接
            conn = JDBCUtils.getConnection();
            // 通过连接创建statement
            sta = conn.prepareCall(sql);

            // 对于in参数,赋值
            sta.setInt(1, 7839);
            // 对于out参数,申明
            sta.registerOutParameter(2, OracleTypes.VARCHAR);
            sta.registerOutParameter(3, OracleTypes.NUMBER);
            sta.registerOutParameter(4, OracleTypes.VARCHAR);

            // 执行调用
            sta.execute();

            // 取出结果
            String name = sta.getString(2);
            double sal = sta.getDouble(3);
            String job = sta.getString(4);
            System.out.println(name + "\t" + sal + "\t" + job);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.release(conn, sta, null);
        }
    }
}
访问存储函数
package test.oracle.demo;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;

import org.junit.Test;

import oracle.jdbc.OracleTypes;
import test.oracle.utils.JDBCUtils;

public class TestFunction {
    /*
    create or replace function queryempannal(pempno in number)
         return number
         as
           psal emp.sal%type;
           pcomm emp.comm%type;
         begin
           select sal,comm into psal, pcomm from emp where empno=pempno ;
           return psal*12+nvl (pcomm,0);
         end;
    */
    @Test
    public void testFunction() {
        // {?= call <procedure-name>[(<arg1>,<arg2>, ...)]}
        String sql = "{?= call queryempannal(?)}";
        Connection conn = null;
        CallableStatement call = null;
        try {
            // 获取数据库连接
            conn = JDBCUtils.getConnection();
            // 通过连接获取statement对象
            call = conn.prepareCall(sql);

            // 对于out参数 声明
            call.registerOutParameter(1, OracleTypes.NUMBER);
            // 对于in参数 赋值
            call.setInt(2, 7839);

            // 执行调用
            call.execute();
            double income = call.getDouble(1);
            System.out.println("该员工的年收入为:" + income);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 释放资源
            JDBCUtils.release(conn, call, null);
        }
    }
}

在out参数中使用光标


申明包结构 , 包头 ,包体。

案例:查询某个部门中所有员工的所有信息。

包头:

create or replace package mypackage01 as
     --使用type关键字,自定义一个光标类型
     type empcursor is ref cursor;
     procedure queryEmpList(dno in number,empList out empcursor);
end mypackage01;

包体:

create or replace package body mypackage01 as
     procedure queryEmpList(dno in number,empList out empcursor) as
     begin
          --打开光标
          open empList for select * from emp where deptno=dno;
     end queryEmpList;
end mypackage01;

在控制台,可以使用desc查看程序包的结构

SQL> desc mypackage01
Element      Type      
------------ --------- 
EMPCURSOR    TYPE      
QUERYEMPLIST PROCEDURE 
在应用中访问包中的存储过程

注意:需要带上包名。

package test.oracle.demo;

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

import org.junit.Test;

import oracle.jdbc.OracleCallableStatement;
import oracle.jdbc.OracleTypes;
import test.oracle.utils.JDBCUtils;

public class TestCursor {

    @Test
    public void testCursor() {
        // {call <procedure-name>[(<arg1>,<arg2>, ...)]}
        String sql = "{call MYPACKAGE01.queryEmpList(?,?)}";
        Connection conn = null;
        CallableStatement call = null;
        ResultSet rs = null;
        try {
            // 获取数据库连接
            conn = JDBCUtils.getConnection();
            // 通过连接获取statement
            call = conn.prepareCall(sql);

            // 对于in 参数 赋值
            call.setInt(1, 12);
            // 对于out参数 申明
            call.registerOutParameter(2, OracleTypes.CURSOR);

            // 执行调用
            call.execute();

            // 获取到指定部门的所有的员工的所有信息
            rs = ((OracleCallableStatement) call).getCursor(2);

            // 遍历结果集
            while (rs.next()) {
                // 获取员工的编号 姓名 薪水 职位
                int empno = rs.getInt("empno");
                String ename = rs.getString("ename");
                double sal = rs.getDouble("sal");
                String job = rs.getString("job");
                System.out.println(empno + "\t" + ename + "\t" + sal + "\t" + job);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.release(conn, call, rs);
        }
    }
}
  • 7
    点赞
  • 0
    评论
  • 64
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

表情包
插入表情
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符
©️2022 CSDN 皮肤主题:技术黑板 设计师:CSDN官方博客 返回首页

打赏作者

挖坑埋你

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值