Oracle存储过程和自定义函数-imooc

2023-01-30 17:19:27 浏览数 (1)

1-1存储过程和存储函数


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

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

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

2-1存储过程


创建和使用存储过程

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

语法:

代码语言:javascript复制
create [or replace] procedure 过程名(参数列表)
as
PLSQL子程序体;

注意事项:

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

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

程序窗口中执行:

代码语言:javascript复制
--第一个存储过程:打印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中执行存储过程,命令窗口中执行:

代码语言:javascript复制
--在命令窗口显示(即打印出来)
SQL> set serveroutput on

--第一种调用方式
SQL> exec sayHelloWorld()
HelloWorld
PL/SQL procedure successfully completed

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

2-3带参数的存储过程

使用的表和数据。命令窗口中执行:

代码语言:javascript复制
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;

创建存储过程。程序窗口中执行:

代码语言:javascript复制
--创建一个带参数的存储过程:
--给指定的员工涨100块钱的工资,并且打印涨前和涨后的薪水
create or replace procedure queryempinform(pempno in emp.empno%type)
as
  --定义变量
  --pename这个引用变量是引用了表emp中的ename字段的类型。
  --如果emp表中ename的类型变了,pename这个字段的类型也会跟着变化
  --引用型变量可以不用知道该表中类型是什么,定义的变量跟着引用表中字段类型改变,易于维护。
  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;

执行存储过程。命令窗口中执行:

代码语言:javascript复制
SQL> set serveroutput on
SQL> 
SQL> begin
  2      queryempinform(7839);
  3      queryempinform(7566);
  4  end;
  5  /

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

PL/SQL procedure successfully completed

注意:

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

如何调试存储过程

  • 调试存储过程最好放到Oracle数据库所在的系统或虚拟机上,解压SQL developer ,双击运行。
  • 为了确保存储过程或函数是可调试的,右键“以编译并进行调试”,点击红色按钮“调试”
  • 利用已写好的调用函数进行调试。
  • 给调试账户授权
代码语言:javascript复制
grant DEBUG CONNECT SESSION ,DEBUG ANY PROCEDURE to scott;

3-1存储函数


存储函数

  • 函数(Function)为一命名的存储程序,可带参数,并返回一计算值;
  • 函数和过程的结构类似,但必须有一个return子句,用于返回函数值。

创建存储函数的语法

代码语言:javascript复制
create [or replace] function 函数名(参数列表)
return 函数值类型
as
PLSQL子程序体;

注意事项:

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

例子:查询某个员工的年收入。程序窗口中执行:

代码语言:javascript复制
--存储函数:查询某个员工的年收入
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;

执行存储函数得到以下结果。命令窗口中执行:

代码语言:javascript复制
SQL> variable a varchar2(20)
SQL> begin
  2  :a:=queryempannal(7839);
  3  end;
  4  /
PL/SQL procedure successfully completed
a
---------
16100

4-1in和out参数


概述

  • 一般来讲,存储过程和存储函数的区别在于存储函数可以有一个返回值,而存储过程没有返回值;
  • 过程和函数都可以通过out指定一个或多个输出参数。我们可以利用out参数,在过程和函数中实现返回多个值; 
    • 存储过程和存储函数都可以有out参数;
    • 存储过程和存储函数都可以有多个out参数;
    • 存储过程可以通过out参数来实现返回值。
  • 什么时候用存储过程/存储函数? 
    • 原则:如果只有一个返回值,用存储函数;否则,就用存储过程。
代码语言:javascript复制
--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;

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


先看一下数据库工具类:

代码语言:javascript复制
package 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:@10.1.124.134:1521:orcl";
	private static String user = "system";
	private static String password = "123456";

    // 注册数据库的驱动
    static {
        try {
			//使用Java反射机制注册
			Class.forName(driver);
			//DriverManager.registerDriver(driver);
		} catch (ClassNotFoundException e) {
			throw new ExceptionInInitializerError(e);
		}
    }

    // 获取数据库连接
    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;
            }
        }
    }
}

访问存储过程

代码语言:javascript复制
package demo.oracle;

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);
        }
    }
}

访问存储函数

代码语言:javascript复制
package demo.oracle;

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);
        }
    }
}

6-1在out参数中使用光标


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

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

包头(申明):

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

包体(实现):

代码语言:javascript复制
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查看程序包的结构

代码语言:javascript复制
SQL> desc mypackage01
Element      Type      
------------ --------- 
EMPCURSOR    TYPE      
QUERYEMPLIST PROCEDURE 

在应用中访问包中的存储过程

注意:需要带上包名。

代码语言:javascript复制
package demo.oracle;

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

import org.junit.Test;

import demo.utils.JDBCUtils;
import oracle.jdbc.OracleCallableStatement;
import oracle.jdbc.internal.OracleTypes;

public class TestCursor {
/*
 * --使用光标创建包头:查询某个部门下的所有员工的所有信息
create or replace package mypackage01 as
     --使用type关键字,自定义一个光标类型
     type empcursor is ref cursor;
     procedure queryEmpList(dno in number,empList out empcursor);
end mypackage01;
*/
	@Test
	public void testCursor(){
        //{call <procedure-name>[(<arg1>,<arg2>, ...)]}
		String sql = "{call mypackage01.queryEmpList(?,?)}";
		Connection conn = null;
		CallableStatement sta = null;
		ResultSet rs = null;
		try {
			conn = JDBCUtils.getConnection();
			sta = conn.prepareCall(sql);
			
			sta.setInt(1, 20);
			sta.registerOutParameter(2, OracleTypes.CURSOR);
			
			sta.execute();
			//取出该部门所有员工信息(强制转换成oracle类型)
			rs = ((OracleCallableStatement)sta).getCursor(2);
			System.out.println("员工号" "t" "姓名" "t" "薪水" "t" "职位");
			while(rs.next()){
				int empno = rs.getInt("empno");
				String name = rs.getString("ename");
				double salary = rs.getDouble("sal");
				String job = rs.getString("job");
				System.out.println(empno "t" name "t" salary "t" job);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally{
			JDBCUtils.release(conn, sta, null);
		}
	}

}

Reference:

  • 慕课网-oracle 存储过程和自定义函数

0 人点赞