Oracle数据库之开发PL/SQL子程序和包

简介: PL/SQL块分为匿名块与命名块,命名块又包含子程序、包和触发器。 过程和函数统称为PL/SQL子程序,我们可以将商业逻辑、企业规则写成过程或函数保存到数据库中,以便共享。 过程和函数均存储在数据库中,并通过参数与其调用者交换信息。

PL/SQL块分为匿名块与命名块,命名块又包含子程序、包和触发器。

过程和函数统称为PL/SQL子程序,我们可以将商业逻辑、企业规则写成过程或函数保存到数据库中,以便共享。

过程和函数均存储在数据库中,并通过参数与其调用者交换信息。过程和函数的唯一区别是函数总向调用者返回数据,而过程不返回数据。

1. 存储过程概念

存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL语句集,存储在数据库中。经过第一次编译后再次调用不需要再次编译,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。

存储过程是数据库中的一个重要对象,任何一个设计良好的数据库应用程序都应该用到存储过程。

2. 创建过程

语法:

1 CREATE [ OR REPLACE ] PROCEDURE [ schema. ] procedure_name
2    [ ( parameter_declaration [, parameter_declaration ]... ) ]
3    [ invoker_rights_clause ]
4    { IS | AS }
5    { [ declare_section ] body | call_spec | EXTERNAL} ;

 

说明:

procedure_name:过程名称。

parameter_declaration:参数声明,格式如下:

parameter_name [ [ IN ] datatype [ { := | DEFAULT } expression ]
          | { OUT | IN OUT } [ NOCOPY ] datatype 

IN:输入参数。

OUT:输出参数。

IN OUT:输入输出参数。

invoker_rights_clause:这个过程使用谁的权限运行,格式:

AUTHID { CURRENT_USER | DEFINER }

declare_section:声明部分。

body:过程块主体,执行部分。

一般只有在确认procedure_name过程是新过程或是要更新的过程时,才使用OR REPALCE关键字,否则容易删除有用的过程。

示例1:

1 CREATE PROCEDURE remove_emp (employee_id NUMBER) AS
2    tot_emps NUMBER;
3    BEGIN
4       DELETE FROM employees
5       WHERE employees.employee_id = remove_emp.employee_id;
6       tot_emps := tot_emps - 1;
7    END;

 示例2:

 1 CREATE OR REPLACE PROCEDURE insert_emp(
 2    v_empno     in employees.employee_id%TYPE,
 3    v_firstname in employees.first_name%TYPE,
 4    v_lastname  in employees.last_name%TYPE,
 5    v_deptno    in employees.department_id%TYPE
 6    )
 7 AS
 8    empno_remaining EXCEPTION;
 9    PRAGMA EXCEPTION_INIT(empno_remaining, -1);
10 BEGIN
11    INSERT INTO EMPLOYEES(EMPLOYEE_ID, FIRST_NAME, LAST_NAME, HIRE_DATE,DEPARTMENT_ID)
12    VALUES(v_empno, v_firstname,v_lastname, sysdate, v_deptno);
13    DBMS_OUTPUT.PUT_LINE('插入成功!');
14 EXCEPTION
15    WHEN empno_remaining THEN
16       DBMS_OUTPUT.PUT_LINE('违反数据完整性约束!');
17       DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);
18    WHEN OTHERS THEN
19       DBMS_OUTPUT.PUT_LINE(SQLCODE||'---'||SQLERRM);
20 END;

 

 

3. 使用过程参数

当建立过程时,既可以指定过程参数,也可以不提供任何参数。

过程参数包括输入参数、输出参数和输入输出参数,其中输入参数(IN)用于接收调用环境的输入数据,输出参数(OUT)用于将输出数据传递到调用环境,而输入输出参数(IN OUT)不仅要接收输入数据,而且还要输出数据到调用环境。

3.1 带有输入参数的过程

通过使用输入参数,可以将应用程序数据传递到过程。当定义过程参数时,默认参数模式是输入参数,另外可以使用IN关键字显式定义输入参数。

示例:

 1 CREATE OR REPLACE PROCEDURE insert_emp(
 2     empno employee.empno%TYPE,
 3     ename employee.ename%TYPE,
 4     job employee.job%TYPE,
 5     sal employee.sal%TYPE,
 6     comm IN employee.comm%TYPE,
 7     deptno IN employee.deptno%TYPE
 8     )
 9 IS
10 BEGIN
11     INSERT INTO employee VALUES(empno, ename, job, sal, comm, depno);
12 END;

 

 

3.2 带有输出参数的过程

通过在过程中使用输出参数,可以将数据或消息传递到调用环境和应用程序。当定义输出参数时,需要指定参数模式OUT。

示例:

1 CREATE OR REPLACE PROCEDURE update_sal(
2     eno NUMBER,
3     salary NUMBER,
4     name out VARCHAR2) 
5 IS
6 BEGIN
7     UPDATE employee SET sal=salary WHERE empno=eno
8     RETURNING ename INTO name;
9 END;

3.3 带有输入输出参数的过程

通过在过程中使用输入输出参数,可以在调用过程时输入数据到过程,在调用结束后输出数据到调用环境和应用程序。当定义输入输出参数时,需要指定参数模式为IN OUT。

示例:

 1 CREATE OR REPLACE PROCEDURE divide(
 2     num1 IN OUT NUMBER,
 3     num2 IN OUT NUMBER) 
 4 IS
 5     v1 NUMBER;
 6     v2 NUMBER;
 7 BEGIN
 8     v1 := trunc(num1 / num2);
 9     v2 := mod(num1,num2);
10     num1 := v1;
11     num2 := v2;
12 END; 

 

 

4. 调用过程

当在SQL*PLUS中调用过程时,需要使用CALL或者EXECUTE命令,而在PL/SQL块中过程可以直接引用。

ORACLE使用EXECUTE语句来调用存储过程语法:

 1 EXEC[UTE] procedure_name(parameter1, parameter2, …); 

示例:

1 -- 调用删除员工的过程
2 EXEC remove_emp(1);
3 
4 -- 调用插入员工的过程
5 EXECUTE insert_emp(1, 'tommy', 'lin', 2);

 

 示例:

1 DECLARE
2     v_name employee.ename%type;
3 BEGIN
4     update_sal(&eno,&salary,v_name);
5     dbms_output.put_line('姓名:'||v_name);
6 END;

 

 

5. 函数介绍

函数是一段独立的PL/SQL程序代码,它执行某个特定的、明确的任务。通常,函数将处理从程序的调用部分传递给它的信息,然后返回单个值。信息通过称为参数的特殊标识符传递给函数,然后通过RETURN语句返回。

6. 创建函数

语法:

 1 CREATE [ OR REPLACE ] FUNCTION [ schema. ] function_name
 2   [ ( parameter_declaration [, parameter_declaration]... ) 
 3   ]
 4   RETURN datatype
 5   [ { invoker_rights_clause
 6     | DETERMINISTIC
 7     | parallel_enable_clause
 8     | RESULT_CACHE  [ relies_on_clause ]
 9     }...
10   ]
11   { { AGGREGATE | PIPELINED }  USING [ schema. ] implementation_type
12   | [ PIPELINED ] { IS | AS } { [ declare_section ] body 
13                               | call_spec
14                               | EXTERNAL
15                               }
16   } ;

 

示例:

1 CREATE FUNCTION get_bal(acc_no IN NUMBER) 
2    RETURN NUMBER 
3    IS
4      acc_bal NUMBER(11,2);
5    BEGIN 
6       SELECT order_total INTO acc_bal FROM orders 
7       WHERE customer_id = acc_no; 
8       RETURN(acc_bal); 
9     END;

 

 

函数参数也有输入、输出、输入输出三种模式:IN、OUT、IN OUT是形参的模式。若省略,则为IN模式。

和过程一样,IN模式的形参只能将实参传递给形参,进入函数内部,但只能读不能写,函数返回时实参的值不变。

OUT模式的形参会忽略调用时的实参值(或说该形参的初始值总是NULL),但在函数内部可以被读或写,函数返回时形参的值会赋予给实参。

IN OUT具有前两种模式的特性,即调用时,实参的值总是传递给形参,结束时,形参的值传递给实参。

调用时,对于IN模式的实参可以是常量或变量,但对于OUT和IN OUT模式的实参必须是变量。

示例:

 1 CREATE OR REPLACE FUNCTION get_salary(
 2   dept_no IN NUMBER DEFAULT 1,
 3   emp_count OUT NUMBER)
 4   RETURN NUMBER
 5 IS
 6   V_sum NUMBER;
 7 BEGIN
 8   SELECT SUM(SALARY), count(*) INTO V_sum, emp_count FROM EMPLOYEES
 9   WHERE DEPARTMENT_ID=dept_no;
10   RETURN v_sum;
11 EXCEPTION
12    WHEN NO_DATA_FOUND THEN
13       DBMS_OUTPUT.PUT_LINE('数据不存在');
14    WHEN OTHERS THEN
15       DBMS_OUTPUT.PUT_LINE('其它异常:');
16       DBMS_OUTPUT.PUT_LINE('错误号:' || SQLCODE||',错误消息:'||SQLERRM);
17 END get_salary;

 

 

7 函数调用

语法:

1 function_name([[parameter_name1 =>] value1[, [parameter_name2 =>] value2, ...]]);

 

示例1:

1 DECLARE
2   v_num NUMBER;
3   v_sum NUMBER;
4 BEGIN
5   v_sum := get_salary(27, v_num);
6   DBMS_OUTPUT.PUT_LINE('部门27的工资总和:'||v_sum||',人数为:'||v_num);
7 END;

 

示例二:

1 DECLARE
2   v_num NUMBER;
3   v_sum NUMBER;
4 BEGIN
5   v_sum := get_salary(dept_no => 27, emp_count => v_num);
6   DBMS_OUTPUT.PUT_LINE('部门27的工资总和:'||v_sum||',人数为:'||v_num);
7 END;

 

示例3:

1 DECLARE
2   v_num NUMBER;
3   v_sum NUMBER;
4 BEGIN
5   v_sum := get_salary(emp_count => v_num);
6   DBMS_OUTPUT.PUT_LINE('部门27的工资总和:'||v_sum||',人数为:'||v_num);
7 END;

 

 

8. 删除过程或函数

删除过程语法:

DROP PROCEDURE [schema.]procudure_name;

删除函数语法:

DROP FUNCTION [schema.]function_name;

9. 过程与函数比较

过程 函数
作为PL/SQL语句执行 作为表达式的一部分执行
在规范中不包含RETURN子句 必须在规范中包含RETURN子句
不返回任何值 必须返回单个值
可以RETURN语句,但是与函数不同,它不能用于返回值 必须包含至少一条RETURN语句

过程与函数的相同功能有:

  1. 都使用IN模式的参数传入数据、OUT模式的参数返回数据。
  2. 输入参数都可以接受默认值,都可以传值或传引导。
  3. 调用时的实际参数都可以使用位置表示法、名称表示法或组合方法。
  4. 都有声明部分、执行部分和异常处理部分。
  5. 其管理过程都有创建、编译、授权、删除、显示依赖关系等。

-- 包

1. 简介

包(PACKAGE)是一种数据对象,它是一组相关过程、函数、变量、常量和游标等PL/SQL程序设计元素的组合,作为一个完整的单元存储在数据库中,用名称来标识。

包类似于JAVA或C#语言中的类,包中的变量相当于类中的成员变量,过程和函数相当于类方法。

通过使用包,可以简化应用程序设计,提高应用性能,实现信息隐藏、子程序重载等面向对象语言所具有的功能。

与高级语言中的类相同,包中的程序元素也分为公用元素和私用元素两种,这两种元素的区别是他们允许访问的程序范围不同。公用元素不仅可以被包中的函数、过程所调用,也可以被包外的PL/SQL程序访问,而私有元素只能被包内的函数和过程序所访问。

一般是先编写独立的过程与函数,待其较为完善或经过充分验证无误后,再按逻辑相关性组织为程序包。

2. 包的优点

  1. 模块化:使用包,可以封装相关的类型、对象和子程序。把一个大的功能模块划分成多个小的功能模块,分别完成各自的功能,这样组织的程序易于编写,理解和管理。
  2. 更轻松的应用程序设计:包规范部分和包体部分可以分别创建并编译。换言之,我们可以在没有编写包体的情况下编写包规范的代码并进行编译。
  3. 信息隐藏:包中的元素可以分为公有元素和私有元素,公有元素可被程序包内的过程、函数等访问,还可以被包外的PL/SQL访问。但对于私有元素只能被包内的过程、函数等访问。对于用户,只需知道包规范,不用了解包体的具体细节。
  4. 性能更佳:应用程序第一次调用程序包中的某个元素时,就将整个程序包加载到内存中,当第二次访问程序包中的元素时,ORACLE将直接从内在中读取,而不需要进行磁盘I/O操作而影响速度,同时位于内存中的程序包可被同一会话期间的其它应用程序共享。因此,程序包增加了重用性并改善了多用户、多应用程序环境的效率。

3. 包的定义

PL/SQL中的包由包规范和包体两部分组成。建立包时,首先要建立包规范,然后再建立对包规范的实现–包体。

包规范用于声明包的公用组件,如变量、常量、自定义数据类型、异常、过程、函数、游标等。包规范中定义的公有组件不仅可以在包内使用,还可以由包外其他过程、函数使用。但需要说明与注意的是,为了实现信息的隐藏,建议不要将所有组件都放在包规范处声明,只应把公共组件放在包规范部分。

包体是包的具体实现细节,它实现在包规范中声明的所有公有过程、函数、游标等。也可以在包体中声明仅属于自己的私有过程、函数、游标等。

3.1 建立包规范

语法:

1 CREATE [ OR REPLACE ] [ EDITIONABLE | NONEDITIONABLE ]
2  PACKAGE [ schema. ] package_name
3   [ invoker_rights_clause ]
4   { IS | AS } item_list_1 END [ package_name ] ;

item_list_1:声明包的公用组件列表

 1 { type_definition -- 数据类型
 2 | cursor_declaration -- 游标
 3 | item_declaration -- 变量、常量等
 4 | function_declaration -- 函数
 5 | procedure_declaration -- 过程
 6 }
 7   [ { type_definition
 8     | cursor_declaration
 9     | item_declaration
10     | function_declaration
11     | procedure_declaration
12     | pragma
13     }
14   ]...

 

示例:
 1 CREATE OR REPLACE PACKAGE emp_mgmt AS 
 2    -- 函数
 3    FUNCTION hire (last_name VARCHAR2, job_id VARCHAR2, 
 4       manager_id NUMBER, salary NUMBER, 
 5       commission_pct NUMBER, department_id NUMBER) 
 6       RETURN NUMBER; 
 7    FUNCTION create_dept(department_id NUMBER, location_id NUMBER) 
 8       RETURN NUMBER; 
 9    -- 过程
10    PROCEDURE remove_emp(employee_id NUMBER); 
11    PROCEDURE remove_dept(department_id NUMBER); 
12    PROCEDURE increase_sal(employee_id NUMBER, salary_incr NUMBER); 
13    PROCEDURE increase_comm(employee_id NUMBER, comm_incr NUMBER); 
14    -- 异常
15    no_comm EXCEPTION; 
16    no_sal EXCEPTION; 
17 END emp_mgmt; 

 

3.2 建立包体
语法:
1 CREATE [ OR REPLACE ] PACKAGE BODY [ schema. ] package_name
2 { IS | AS } 
3     BEGIN statement [ statement | pragma ]...
4       [ EXCEPTION exception_handler [ exception_handler ]... ]
5  [ initialize_section ]
6 END [ package_name ] ;

 

示例:
 1 CREATE OR REPLACE PACKAGE BODY emp_mgmt AS 
 2    tot_emps NUMBER; 
 3    tot_depts NUMBER; 
 4 FUNCTION hire 
 5    (last_name VARCHAR2, job_id VARCHAR2, 
 6     manager_id NUMBER, salary NUMBER, 
 7     commission_pct NUMBER, department_id NUMBER) 
 8    RETURN NUMBER IS new_empno NUMBER; 
 9 BEGIN 
10    SELECT employees_seq.NEXTVAL 
11       INTO new_empno 
12       FROM DUAL; 
13    INSERT INTO employees 
14       VALUES (new_empno, 'First', 'Last','first.example@example.com', 
15               '(415)555-0100','18-JUN-02','IT_PROG',90000000,00, 
16               100,110); 
17       tot_emps := tot_emps + 1; 
18    RETURN(new_empno); 
19 END; 
20 FUNCTION create_dept(department_id NUMBER, location_id NUMBER) 
21    RETURN NUMBER IS 
22       new_deptno NUMBER; 
23    BEGIN 
24       SELECT departments_seq.NEXTVAL 
25          INTO new_deptno 
26          FROM dual; 
27       INSERT INTO departments 
28          VALUES (new_deptno, 'department name', 100, 1700); 
29       tot_depts := tot_depts + 1; 
30       RETURN(new_deptno); 
31    END; 
32 PROCEDURE remove_emp (employee_id NUMBER) IS 
33    BEGIN 
34       DELETE FROM employees 
35       WHERE employees.employee_id = remove_emp.employee_id; 
36       tot_emps := tot_emps - 1; 
37    END; 
38 PROCEDURE remove_dept(department_id NUMBER) IS 
39    BEGIN 
40       DELETE FROM departments 
41       WHERE departments.department_id = remove_dept.department_id; 
42       tot_depts := tot_depts - 1; 
43       SELECT COUNT(*) INTO tot_emps FROM employees; 
44    END; 
45 PROCEDURE increase_sal(employee_id NUMBER, salary_incr NUMBER) IS 
46    curr_sal NUMBER; 
47    BEGIN 
48       SELECT salary INTO curr_sal FROM employees 
49       WHERE employees.employee_id = increase_sal.employee_id; 
50       IF curr_sal IS NULL 
51          THEN RAISE no_sal; 
52       ELSE 
53          UPDATE employees 
54          SET salary = salary + salary_incr 
55          WHERE employee_id = employee_id; 
56       END IF; 
57    END; 
58 PROCEDURE increase_comm(employee_id NUMBER, comm_incr NUMBER) IS 
59    curr_comm NUMBER; 
60    BEGIN 
61       SELECT commission_pct 
62       INTO curr_comm 
63       FROM employees 
64       WHERE employees.employee_id = increase_comm.employee_id; 
65       IF curr_comm IS NULL 
66          THEN RAISE no_comm; 
67       ELSE 
68          UPDATE employees 
69          SET commission_pct = commission_pct + comm_incr; 
70       END IF; 
71    END; 
72 END emp_mgmt;

4. 调用包的组件

包的名称是唯一的,但对于两个包中的公有组件的名称可以相同,用“包名.公有组件名“加以区分。

示例:

 1 DECLARE
 2     new_dno NUMBER; -- 部门编号
 3 BEGIN
 4     -- 调用emp_mgmt包的create_dept函数创建部门:
 5     new_dno := emp_mgmt.create_dept(85, 100);
 6     DBMS_OUTPUT.PUT_LINE('部门编号:' || new_dno);
 7 
 8     -- 调用emp_mgmt包的increase_sal过程为员工加薪:
 9     emp_mgmt.increase_sal(23, 800);
10 END;

5. 包中的游标

在包中使用无参游标,示例:

 1 --定义包规范
 2 CREATE OR REPLACE PACKAGE PKG_STU IS
 3     CURSOR getStuInfo RETURN stuInfo%ROWTYPE; 
 4 END PKG_STU;
 5 
 6 --定义包体
 7 CREATE OR REPLACE PACKAGE BODY PKG_STU AS
 8     CURSOR getStuInfo RETURN stuInfo%ROWTYPE IS
 9         SELECT * FROM stuInfo; 
10 END PKG_STU;
11 
12 --调用包组件
13 BEGIN
14     FOR stu_Record IN PKG_STU.getStuInfo LOOP  
15        DBMS_OUTPUT.PUT_LINE('学员姓名:'||stu_Record.name||',学号:'||stu_Record.id||',年龄:'||stu_Record.age);      
16     END LOOP; 
17 END;  

在包中使用有参数的游标,示例:

 1 --定义包规范
 2 CREATE OR REPLACE PACKAGE PKG_STU IS
 3     CURSOR getStuInfo(studentNo VARCHAR2) RETURN stuInfo%ROWTYPE; 
 4 END PKG_STU;
 5 
 6 --定义包体
 7 CREATE OR REPLACE PACKAGE BODY PKG_STU AS
 8     CURSOR getStuInfo(studentNo VARCHAR2) RETURN stuInfo%ROWTYPE IS
 9         SELECT * FROM stuInfo WHERE id=studentNo; 
10 END PKG_STU;
11 
12 --调用包组件
13 BEGIN
14     FOR stu_Record IN PKG_STU.getStuInfo(2) LOOP  
15         DBMS_OUTPUT.PUT_LINE('学员姓名:'||stu_Record.name||',学号:'||stu_Record.id||',年龄:'||stu_Record.age);      
16     END LOOP; 
17 END;

由于游标变量是一个指针,其状态是不确定的,因此它不能随同包存储在数据库中,即不能在PL/SQL包中声明游标变量。但在包中可以创建游标变量参照类型,并可向包中的子程序传递游标变量参数。

示例:

 1 -- 创建包规范
 2 CREATE OR REPLACE PACKAGE CURROR_VARIBAL_PKG AS
 3   TYPE dept_cur_type IS REF CURSOR RETURN dept%ROWTYPE; --强类型
 4 
 5   TYPE cur_type IS REF CURSOR;-- 弱类型
 6 
 7   PROCEDURE proc_open_dept_var(
 8     dept_cur IN OUT dept_cur_type,
 9     choice INTEGER DEFAULT 0,
10     dept_no NUMBER DEFAULT 50,
11     dept_name VARCHAR DEFAULT '%');
12 END;
13 
14 -- 创建包体
15 CREATE OR REPLACE PACKAGE BODY CURROR_VARIBAL_PKG
16 AS
17   PROCEDURE proc_open_dept_var(
18     dept_cur IN OUT dept_cur_type,
19     choice INTEGER DEFAULT 0,
20     dept_no NUMBER DEFAULT 50,
21     dept_name VARCHAR DEFAULT '%')
22   IS 
23   BEGIN
24     IF choice = 1 THEN
25       OPEN dept_cur FOR SELECT * FROM dept WHERE deptno = dept_no;
26     ELSIF choice = 2 THEN
27       OPEN dept_cur FOR SELECT * FROM dept WHERE dname LIKE dept_name;
28     ELSE
29       OPEN dept_cur FOR SELECT * FROM dept;
30     END IF;
31   END proc_open_dept_var;
32 END CURROR_VARIBAL_PKG;

 

 
 1 定义一个过程,打开弱类型的游标变量:
 2 
 3 --定义过程
 4 CREATE OR REPLACE PROCEDURE proc_open_cur_type(
 5   cur IN OUT CURROR_VARIBAL_PKG.cur_type,
 6   first_cap_in_table_name CHAR) 
 7 AS
 8 BEGIN
 9   IF first_cap_in_table_name = 'D' THEN
10     OPEN cur FOR SELECT * FROM dept;
11   ELSE
12     OPEN cur FOR SELECT * FROM emp;
13   END IF;
14 END proc_open_cur_type;
 1 测试包中游标变量类型的使用:
 2 
 3 DECLARE 
 4   dept_rec Dept%ROWTYPE;
 5   emp_rec Emp%ROWTYPE;
 6   dept_cur CURROR_VARIBAL_PKG.dept_cur_type;
 7   cur CURROR_VARIBAL_PKG.cur_type;
 8 BEGIN
 9   DBMS_OUTPUT.PUT_LINE('游标变量强类型:');
10   CURROR_VARIBAL_PKG.proc_open_dept_var(dept_cur, 1, 30);
11   FETCH dept_cur INTO dept_rec;
12   WHILE dept_cur%FOUND LOOP
13     DBMS_OUTPUT.PUT_LINE(dept_rec.deptno||':'||dept_rec.dname);
14     FETCH dept_cur INTO dept_rec;
15   END LOOP;
16   CLOSE dept_cur;
17 
18   DBMS_OUTPUT.PUT_LINE('游标变量弱类型:');
19   CURROR_VARIBAL_PKG.proc_open_dept_var(cur, 2, dept_name => 'A%');
20   FETCH cur INTO dept_rec;
21   WHILE cur%FOUND LOOP
22     DBMS_OUTPUT.PUT_LINE(dept_rec.deptno||':'||dept_rec.dname);
23     FETCH cur INTO dept_rec;
24   END LOOP;
25 
26   DBMS_OUTPUT.PUT_LINE('游标变量弱类型—dept表:');
27   proc_open_cur_type(cur, 'D');
28   FETCH cur INTO dept_rec;
29   WHILE cur%FOUND LOOP
30     DBMS_OUTPUT.PUT_LINE(dept_rec.deptno||':'||dept_rec.dname);
31     FETCH cur INTO dept_rec;
32   END LOOP;
33 
34   DBMS_OUTPUT.PUT_LINE('游标变量弱类型—emp表:');
35   proc_open_cur_type(cur, 'E');
36   FETCH cur INTO emp_rec;
37   WHILE cur%FOUND LOOP
38     DBMS_OUTPUT.PUT_LINE(emp_rec.empno||':'||emp_rec.ename);
39     FETCH cur INTO emp_rec;
40   END LOOP;
41   CLOSE cur;
42 END;

 

 

6. 子程序重载

所谓重载时指两个或多个子程序有相同的名称,但拥有不同的参数变量、参数顺序或参数数据类型。

在调用重载子程序时,主程序将根据实际参数的类型和数目,自动确定调用哪个子程序。

PL/SQL允许对包内子程序和本地子程序进行重载。

示例:

 1 -- 定义包规范
 2 CREATE OR REPLACE PACKAGE PKG_EMP AS
 3     FUNCTION get_salary(eno NUMBER) RETURN NUMBER;
 4     FUNCTION get_salary(empname VARCHAR2) RETURN NUMBER;
 5 END PKG_EMP;
 6 
 7 -- 定义包体
 8 CREATE OR REPLACE PACKAGE BODY PKG_EMP AS
 9     FUNCTION get_salary(eno NUMBER) RETURN NUMBER
10       IS
11         v_salary NUMBER(10, 4);
12       BEGIN
13         SELECT sal INTO v_salary FROM emp WHERE empno=eno;
14         RETURN v_salary;
15       END;
16 
17     FUNCTION get_salary(empname VARCHAR2) RETURN NUMBER
18       IS
19         v_salary NUMBER(10, 4);
20       BEGIN
21         SELECT sal INTO v_salary FROM emp WHERE ename=empname;
22         RETURN v_salary;
23       END;
24 END PKG_EMP;

 

 

 1 测试:
 2 
 3 DECLARE
 4   v_sal NUMBER(10, 4);
 5 BEGIN  
 6   v_sal := PKG_EMP.get_salary(7499);
 7   DBMS_OUTPUT.PUT_LINE('工资:' || v_sal);
 8   v_sal := PKG_EMP.get_salary('MARTIN');
 9   DBMS_OUTPUT.PUT_LINE('工资:' || v_sal);
10 END; 

 

相关文章
|
4天前
|
SQL Oracle 关系型数据库
sql语句创建数据库
在创建数据库之前,请确保你有足够的权限,并且已经考虑了数据库的安全性和性能需求。此外,不同的DBMS可能有特定的最佳实践和配置要求,因此建议查阅相关DBMS的官方文档以获取更详细和准确的信息。
|
4天前
|
SQL 存储 关系型数据库
数据库开发之图形化工具以及表操作的详细解析
数据库开发之图形化工具以及表操作的详细解析
21 0
|
4天前
|
SQL 存储 关系型数据库
数据库开发之mysql前言以及详细解析
数据库开发之mysql前言以及详细解析
14 0
|
2天前
|
SQL Java 数据库连接
Java从入门到精通:2.3.2数据库编程——了解SQL语言,编写基本查询语句
Java从入门到精通:2.3.2数据库编程——了解SQL语言,编写基本查询语句
|
4天前
|
SQL 缓存 数据库
sql 数据库优化
SQL数据库优化是一个复杂且关键的过程,涉及多个层面的技术和策略。以下是一些主要的优化建议: 查询语句优化: 避免全表扫描:在查询时,尽量使用索引来减少全表扫描,提高查询速度。 使用合适的子查询方式:子查询可能降低查询效率,但可以通过优化子查询的结构或使用连接(JOIN)替代子查询来提高性能。 简化查询语句:避免不必要的复杂查询,尽量使SQL语句简单明了。 使用EXISTS替代IN:在查询数据是否存在时,使用EXISTS通常比IN更快。 索引优化: 建立合适的索引:对于经常查询的列,如主键和外键,应创建相应的索引。同时,考虑使用覆盖索引来进一步提高性能。 避免过多的索引:虽然索引可以提高查询
|
4天前
|
SQL XML 数据库
sql导入数据库命令
在SQL Server中,数据库导入可通过多种方式实现:1) 使用SSMS的“导入数据”向导从各种源(如Excel、CSV)导入;2) BULK INSERT语句适用于导入文本文件;3) bcp命令行工具进行批量数据交换;4) OPENROWSET函数直接从外部数据源(如Excel)插入数据。在操作前,请记得备份数据库,并可能需对数据进行预处理以符合SQL Server要求。注意不同方法可能依版本和配置而异。
|
7天前
|
SQL Oracle 关系型数据库
Oracle的PL/SQL游标属性:数据的“导航仪”与“仪表盘”
【4月更文挑战第19天】Oracle PL/SQL游标属性如同车辆的导航仪和仪表盘,提供丰富信息和控制。 `%FOUND`和`%NOTFOUND`指示数据读取状态,`%ROWCOUNT`记录处理行数,`%ISOPEN`显示游标状态。还有`%BULK_ROWCOUNT`和`%BULK_EXCEPTIONS`增强处理灵活性。通过实例展示了如何在数据处理中利用这些属性监控和控制流程,提高效率和准确性。掌握游标属性是提升数据处理能力的关键。
|
7天前
|
SQL Oracle 安全
Oracle的PL/SQL循环语句:数据的“旋转木马”与“无限之旅”
【4月更文挑战第19天】Oracle PL/SQL中的循环语句(LOOP、EXIT WHEN、FOR、WHILE)是处理数据的关键工具,用于批量操作、报表生成和复杂业务逻辑。LOOP提供无限循环,可通过EXIT WHEN设定退出条件;FOR循环适用于固定次数迭代,WHILE循环基于条件判断执行。有效使用循环能提高效率,但需注意避免无限循环和优化大数据处理性能。掌握循环语句,将使数据处理更加高效和便捷。
|
7天前
|
SQL Oracle 关系型数据库
Oracle的PL/SQL条件控制:数据的“红绿灯”与“分岔路”
【4月更文挑战第19天】在Oracle PL/SQL中,IF语句与CASE语句扮演着数据流程控制的关键角色。IF语句如红绿灯,依据条件决定程序执行路径;ELSE和ELSIF提供多分支逻辑。CASE语句则是分岔路,按表达式值选择执行路径。这些条件控制语句在数据验证、错误处理和业务逻辑中不可或缺,通过巧妙运用能实现高效程序逻辑,保障数据正确流转,支持企业业务发展。理解并熟练掌握这些语句的使用是成为合格数据管理员的重要一环。
|
7天前
|
SQL Oracle 关系型数据库
Oracle的PL/SQL表达式:数据的魔法公式
【4月更文挑战第19天】探索Oracle PL/SQL表达式,体验数据的魔法公式。表达式结合常量、变量、运算符和函数,用于数据运算与转换。算术运算符处理数值计算,比较运算符执行数据比较,内置函数如TO_CHAR、ROUND和SUBSTR提供多样化操作。条件表达式如CASE和NULLIF实现灵活逻辑判断。广泛应用于SQL查询和PL/SQL程序,助你驾驭数据,揭示其背后的规律与秘密,成为数据魔法师。

推荐镜像

更多