Oracle PL/SQL基础知识

来源:互联网 发布:mac查看环境变量 编辑:程序博客网 时间:2024/06/06 07:28

Oracle PL/SQL基础知识

过程(存储过程)

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

 

调用过程有两种方法:

exec 过程名(参数值,..)

call 过程名(参数值,..)

 

创建存储过程基本语法:

create or replace procedure 过程名(变量 in 变量类型,..,变量 out 变量类型) is

//定义变量

begin

//执行语句;

end;

/

特别说明:or replace在创建存储过程中可带也可不。带or replace是指在存储过程名字相同时其覆盖。不带则无法覆盖。在使用or replace时要小心,建议不使用or replace对存储过程进覆盖。

 

过程创建举例:

[sql] view plain copy
  1. --输入员工编号,新工资,修改员工的工资  
  2. create or replace procedure p4(v_in_emp in number,v_in_new_sal in number) is  
  3. v_ename varchar2(30);  
  4. begin  
  5.   select ename into v_ename from emp2 where empno = v_in_emp;  
  6.   update emp2 set sal = v_in_new_sal where empno = v_in_emp;  
  7. exception  
  8.   when no_data_found then  
  9.        dbms_output.put_line('员工编号不存在');  
  10. end;  
  11. /  

 

特别说明:当编写过程出现错误时,查看具体错误信息。输入show error;

 

[java] view plain copy
  1. Java调用过程  
  2. /** 
  3.  * 调用过程 
  4.  */  
  5. package com.oracle.db;  
  6. import java.sql.*;  
  7. public class db4 {  
  8.     // 调用p4过程  
  9.     public static void main(String[] args) {  
  10.         Connection ct = null;  
  11.         CallableStatement cs = null;  
  12.         try {  
  13.             // 加载驱动  
  14.             Class.forName("oracle.jdbc.driver.OracleDriver");  
  15.             // 得到连接  
  16.             ct = DriverManager.getConnection(  
  17.                     "jdbc:oracle:thin:@127.0.0.1:1521:Switch""scott",  
  18.                     "123456");  
  19.             // 创建CallableStatement  
  20.             cs = ct.prepareCall("{call p4(?,?)}");  
  21.             // 给?赋值  
  22.             cs.setInt(17369);  
  23.             cs.setFloat(21000.5f);  
  24.             // 执行  
  25.             cs.execute();  
  26.         } catch (Exception e) {  
  27.             e.printStackTrace();  
  28.         } finally {  
  29.             try {  
  30.                 // 关闭资源  
  31.                 if (cs != null) {  
  32.                     cs.close();  
  33.                 }  
  34.                 if (ct != null) {  
  35.                     ct.close();  
  36.                 }  
  37.             } catch (Exception e2) {  
  38.                 e2.printStackTrace();  
  39.             }  
  40.         }  
  41.     }  
  42. }  


扩展SQLHelper类,使之实现无返回参数的存储过程的方法

[java] view plain copy
  1. package com.oracle.db;  
  2. import java.io.FileInputStream;  
  3. import java.sql.*;  
  4. import java.util.Properties;  
  5.   
  6. public class SQLHelper {  
  7.     // 定义变量  
  8.     private static Connection ct = null;  
  9.     private static PreparedStatement ps = null;  
  10.     private static ResultSet rs = null;  
  11.     private static CallableStatement cs = null;  
  12.   
  13.     // 连接数据库的用户名,密码,url,驱动  
  14.     // 说明:在实际开发中,我们往往把这些变量写到一个外部文件中  
  15.     // 当程序启动时,我们读入这些配置信息。java.util.Properites  
  16.     private static String username;  
  17.     private static String password;  
  18.     private static String driver;  
  19.     private static String url;  
  20.   
  21.     // 使用静态块加载驱动(驱动只需要加载一次)  
  22.     static {  
  23.         // 使用Properties类,来读取配置文件  
  24.         Properties pp = new Properties();  
  25.         FileInputStream fis = null;  
  26.         try {  
  27.             fis = new FileInputStream("dbinfo.properties");  
  28.             // 让pp与dbinfo.properties文件关联起来  
  29.             pp.load(fis);  
  30.             // 获取dbinfo.properties文件内信息  
  31.             username = (String) pp.getProperty("username");  
  32.             password = (String) pp.getProperty("password");  
  33.             driver = (String) pp.getProperty("driver");  
  34.             url = (String) pp.getProperty("url");  
  35.   
  36.             // 获得驱动  
  37.             Class.forName(driver);  
  38.         } catch (Exception e) {  
  39.             e.printStackTrace();  
  40.         } finally {  
  41.             try {  
  42.                 if (fis != null) {  
  43.                     fis.close();  
  44.                 }  
  45.             } catch (Exception e) {  
  46.                 e.printStackTrace();  
  47.             }  
  48.             fis = null;  
  49.         }  
  50.     }  
  51.   
  52.     // 调用存储过程的方法  
  53.     public static void executeProcedure(String sql, String[] parameters) {  
  54.         try {  
  55.             ct = DriverManager.getConnection(url,username,password);  
  56.             cs = ct.prepareCall(sql);  
  57.             if (parameters != null && !"".equals(parameters)) {  
  58.                 for(int i = 0; i < parameters.length; i++){  
  59.                     cs.setString(i + 1, parameters[i]);  
  60.                 }  
  61.             }  
  62.             //执行  
  63.             cs.execute();  
  64.         } catch (Exception e) {  
  65.             e.printStackTrace();  
  66.             throw new RuntimeException(e.getMessage());  
  67.         } finally {  
  68.             close(rs, cs, ct);  
  69.         }  
  70.     }  
  71.   
  72.     // 统一的cud操作  
  73.     public static void executeUpdate(String sql, String[] parameters) {  
  74.         try {  
  75.             ct = DriverManager.getConnection(url, username, password);  
  76.             ps = ct.prepareStatement(sql);  
  77.             if (parameters != null) {  
  78.                 for (int i = 0; i < parameters.length; i++) {  
  79.                     ps.setString(i + 1, parameters[i]);  
  80.                 }  
  81.             }         
  82.             // 执行  
  83.             ps.executeUpdate();  
  84.   
  85.         } catch (Exception e) {  
  86.             e.printStackTrace();  
  87.             throw new RuntimeException(e.getMessage());  
  88.         } finally {  
  89.             close(rs, ps, ct);  
  90.         }  
  91.     }  
  92.   
  93.     // 写一个方法,完成查询任务  
  94.     // sql表示要执行的sql语句  
  95.     // sql select * from emp where ename=?  
  96.     public static ResultSet executeQuery(String sql, String[] parameters) {  
  97.         try {  
  98.             // 根据实际情况我们对sql语句?赋值  
  99.             // 得到连接  
  100.             ct = DriverManager.getConnection(url, username, password);  
  101.             // 创建ps对象,得到sql语句对象  
  102.             ps = ct.prepareStatement(sql);  
  103.             // 如果parameters不为null,才赋值  
  104.             if (parameters != null) {  
  105.                 for (int i = 0; i < parameters.length; i++) {  
  106.                     ps.setString(i + 1, parameters[i]);  
  107.                 }  
  108.             }  
  109.             rs = ps.executeQuery();  
  110.         } catch (SQLException e) {  
  111.             e.printStackTrace();  
  112.             // 抛出运行异常  
  113.             throw new RuntimeException(e.getMessage());  
  114.         } finally {  
  115.             // close(rs,ps,ct);  
  116.         }  
  117.         return rs;  
  118.     }  
  119.   
  120.     // 把关闭资源写成函数  
  121.     public static void close(ResultSet rs, Statement ps, Connection ct) {  
  122.         // 关闭资源  
  123.         if (rs != null) {  
  124.             try {  
  125.                 rs.close();  
  126.             } catch (SQLException e) {  
  127.                 e.printStackTrace();  
  128.             }  
  129.             rs = null;  
  130.         }  
  131.         if (ps != null) {  
  132.             try {  
  133.                 ps.close();  
  134.             } catch (SQLException e) {  
  135.                 e.printStackTrace();  
  136.             }  
  137.             ps = null;  
  138.         }  
  139.         if (ct != null) {  
  140.             try {  
  141.                 ct.close();  
  142.             } catch (SQLException e) {  
  143.                 e.printStackTrace();  
  144.             }  
  145.             ct = null;  
  146.         }  
  147.     }  
  148.   
  149.     public static Connection getCt() {  
  150.         return ct;  
  151.     }  
  152.   
  153.     public static PreparedStatement getPs() {  
  154.         return ps;  
  155.     }  
  156. }  

dbinfo.propeties中的属性

driver = oracle.jdbc.driver.OracleDriver

url = jdbc:oracle:thin:@127.0.0.1:1521:Switch

username = scott

password =123456


[java] view plain copy
  1. dbinfo.propeties中的属性  
  2. driver = oracle.jdbc.driver.OracleDriver  
  3. url = jdbc:oracle:thin:@127.0.0.1:1521:Switch  
  4. username = scott  
  5. password =123456  

函数

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

建立函数的基本语法:

create function 函数名(参数1,...)

return 数据类型 is

定义变量;

begin

执行语句;

end;

/

 

函数调用的基本语法:

select 函数名(参数,...) from dual;

 

实例:

[sql] view plain copy
  1. --用函数接收用户名,返回用户年薪  
  2. create or replace function f1(v_in_ename varchar2)  
  3. return number is  
  4. v_annual_sal number;  
  5. begin  
  6.   select (sal+nvl(comm,0))*13 into v_annual_sal from emp2 where ename  = v_in_ename;  
  7.   return v_annual_sal;  
  8. end;  
  9. /  

函数和过程的区别:

1、函数必须有返回值,而过程可以没有;

2、函数和过程在java中调用的方式不一样;

java中调用oracle函数可以在select语句中直接调用,如:select 自定义的函数名(参数) from 表;

过程则是使用CallableStatement完成调用。

[java] view plain copy
  1. /** 
  2.  * 使用SQLHelper调用函数 
  3.  */  
  4. package com.oracle.db;  
  5. import java.sql.*;  
  6. public class db6 {  
  7.     public static void main(String[] args) {  
  8.         String sql = "select f1('SMITH') from dual";  
  9.         ResultSet rs = SQLHelper.executeQuery(sql, null);  
  10.         try {  
  11.             if(rs.next()){  
  12.                 System.out.println(rs.getDouble(1));  
  13.             }  
  14.         } catch (Exception e) {  
  15.             // TODO: handle exception  
  16.             e.printStackTrace();  
  17.         }  
  18.     }  
  19. }  

包用于在逻辑上组合过程和函数等对象,它由包的规范和包体两部分组成。

1、使用create package命令来创建包

建包基本语法:

create [or replace] package 包名 is

procedure 过程名(变量名 变量类型,...);

function 函数名(变量名 变量类型,...) return 数据类型;

end;

/

包的规范只包含了过程和函数的说明,但是没有过程和函数的实现代码。包体用于实现包规范的过程和函数。

 

实例:

[sql] view plain copy
  1. --声明一个包  
  2. create or replace package pack1 is  
  3. --声明一个过程用于输入员工姓名,新工资,修改员工的工资  
  4.        procedure p1(v_in_ename in varchar2,v_in_newsal number);  
  5. --声明一个函数用于接收用户名,返回用户年薪  
  6.        function f1(v_in_ename in varchar2) return number;  
  7. end;  
  8. /  

2、建立包体可以使用create package body 命令

建立包体基本语法:

create or replace package body 包名 is

procedure 过程名(变量名 变量类型,...) is

--声明变量;

begin

--执行语句;

exception

when 异常名 then

--异常处理;

end;

function 函数名(变量名 变量类型,...)

return 数据类型 is

--声明变量;

begin

--执行语句;

end;

end;

/


实例:

[sql] view plain copy
  1. --建立包体  
  2. create or replace package body pack1 is  
  3. --实现一个过程用于输入员工姓名,新工资,修改员工的工资  
  4.        procedure p1(v_in_ename in varchar2,v_in_newsal number) is  
  5.        begin  
  6.          update emp2 set sal = v_in_newsal where ename = v_in_ename;  
  7.        end;  
  8. --实现一个函数用于接收用户名,返回用户年薪  
  9.        function f1(v_in_ename in varchar2) return number is  
  10.        v_annual_sal number;  
  11.        begin  
  12.          select (sal+nvl(comm,0))*13 into v_annual_sal from emp2 where ename = v_in_ename;  
  13.        return v_annual_sal;  
  14.        end;  
  15. end;  
  16. /  

细节说明:

1、包体中要实现的函数或过程,应当在包规范中声明;

2、在调用包中的某个函数或过程的时候,需要使用对应的方法才可以调用。

3、如何调用包的过程或函数

当调用包的过程或是函数时,在过程和函数前需要带有包名,如果要访问其它方案的包,还需要在包名前加方案名。

调用基本方法:

exec 方案名.包名.过程名(参数,...);

call 方案名.包名.函数名(参数,...);

也可以直接用select 方案名.包名.函数名(参数,...) from dual;


[sql] view plain copy
  1. /**  
  2.  * 使用SQLHelper调用包中函数  
  3.  */  
  4. package com.oracle.db;  
  5. import java.sql.*;  
  6. public class db7 {  
  7.     public static void main(String[] args) {  
  8.         String sql = "select scott.pack1.f1('SMITH') from dual";  
  9.         ResultSet rs = SQLHelper.executeQuery(sql, null);  
  10.         try {  
  11.             if(rs.next()){  
  12.                 System.out.println(rs.getDouble(1));  
  13.             }  
  14.         } catch (Exception e) {  
  15.             // TODO: handle exception  
  16.             e.printStackTrace();  
  17.         }  
  18.     }  
  19. }  

触发器

触发器是指隐含的执行的存储过程。当定义触发器时,必须要指定触发的事件和触发的操作,常用的触发事件包括insert/update/delete语句,而触发操作实际就是一个pl/sql块。可以使用create trigger 来建立触发器。

PS:触发器也是用来维护表的完整性的,触发器将在之后单独写成一篇博客。

定义并使用变量

在编写pl/sql程序时,可以定义变量和常量;在pl/sql程序中包括有:

1、标量类型(scalar)

2、复合类型(composite)

3、参照类型(reference)

4、lob(large object)

标量(scalar)

在编写pl/sql块时,如果要使用变量,需在定义部分定义变量。

pl/sql中定义变量和常量的语法如下:

identifier [constant] datatype [not null] [:=|default expr]

说明:

identifier:名称

constant:指定常量。需要指定它的初始值,且其值是不能改变的。

datatype:数据类型

not null:指定变量值不能为null

:=给变量或是常量指定初始值

default:用于指定初始值

expr:指定初始值的pl/sql表达式,可是文本值、其它变量、函数等。

 

标量定义的案例:

1、定义一个变长字符串

v_ename varchar2(10);

2、定义一个小数范围-9999.99~9999.99

v_sal number(6,2);

3、定义一个小数并给一个初始值为5.4 :=是pl/sql的赋值号

v_sal2 number(6,2):=5.4

4、定义一个日期类型的数据

v_hiredate date;

5、定义一个布尔变量,不能为空,初始值为false

v_valid boolean not null default false;

特别说明:pl/sql在定义一个变量的时候,如果要赋初值,则需要使用:=,如果只是=则是用于判断两个值是否相等。


实例:

[sql] view plain copy
  1. --编写过程,以输入的员工号,显示员工的姓名、工资、个人所得税(税率为0.03)  
  2. create or replace procedure p5(v_in_empno in number) is  
  3. v_ename varchar2(30);  
  4. v_sal number;  
  5. v_tax_rate number(3,2) := 0.03;  
  6. v_tax number;  
  7. begin  
  8.   select ename,sal into v_ename,v_sal from emp2 where empno = v_in_empno;  
  9.   v_tax := v_sal * v_tax_rate;  
  10.   dbms_output.put_line('员工的姓名为:' || v_ename || ' 员工的工资为:' || v_sal || ' 员工的个人所得税为: ' || v_tax);  
  11. end;  
  12. /  

%type类型

为了降低pl/sql程序的维护工作量,可以使用%type属性定义变量,这样它会按照数据库列来确定你定义的变量的类型和长度。

 

%type类型使用的基本语法:

标识符名 表名.列名%type;

[sql] view plain copy
  1. --优化  
  2. create or replace procedure p6(v_in_empno in number) is  
  3. v_ename emp2.ename%type;    --使用emp2中ename的类型定义  
  4. v_sal emp2.sal%type;        --使用emp2中sal的类型定义  
  5. v_tax_rate emp2.sal%type := 0.03;   --使用emp2中sal的类型定义  
  6. v_tax emp2.sal%type;        --使用emp2中sal的类型定义  
  7. begin  
  8.   select ename,sal into v_ename,v_sal from emp2 where empno = v_in_empno;  
  9.   v_tax := v_sal * v_tax_rate;  
  10.   dbms_output.put_line('员工的姓名为:' || v_ename || ' 员工的工资为:' || v_sal || ' 员工的个人所得税为: ' || v_tax);  
  11. end;  
  12. /  

复合变量(composite)

用于存放多个值的变量。常用的包括:1、pl/sql记录;2、pl/sql表

 

复合类型--pl/sql记录

类似与高级语言中的结构体,需要注意的是,当引用pl/sql记录成员时,必须要加记录变量作为前缀(记录变量.记录成员)如下:

复合变量定义基本语法:

type 自定义的pl/sql记录名 is record(

变量名 变量类型,

变量名 变量类型

);

 

复合变量基本使用语法:

变量名 自定义的pl/sql记录名;

 

[sql] view plain copy
  1. --复合类型  
  2. --编写过程,接收员工编号,显示该员工的名字、薪水、工作岗位(用pl/sql记录实现)  
  3. create or replace procedure p7(v_in_empno in emp2.ename%type) is  
  4. --定义记录类型  
  5. type emps_record is record  
  6. (  
  7.      v_ename emp2.ename%type,  
  8.      v_sal emp2.sal%type,  
  9.      v_job emp2.job%type  
  10. );  
  11. --定义emps_record记录类型的变量  
  12. emps emps_record;  
  13. begin  
  14.   select ename,sal,job into emps.v_ename,emps.v_sal,emps.v_job from emp2 where empno = v_in_empno;  
  15.   dbms_output.put_line('员工的姓名为:' || emps.v_ename || ' 员工的工资为:' || emps.v_sal || ' 员工的工作为: ' || emps.v_job);  
  16. end;  
  17. /  

复合类型--pl/sql

相当于高级语言中的数组,但是需要注意的是在高级语言中数组的下标不能为负数,而pl/sql是可以为负数的,并且表元素的下标没有限制。(可以理解为是oracle下的数组)实例如下:

复合类型pl/sql表的基本语法:

type 自定义的pl/sql表名 is table of 对应表.列名%type

index by binary_integer;


[sql] view plain copy
  1. declare  
  2. type table_type is table of emp2.ename%type  
  3. index by binary_integer;  
  4. stable table_type;--定义一个变量  
  5. begin  
  6. select ename into stable(-1) from emp where empno=7788;  
  7. dbms_output.put_line('员工名:'||stable(-1));  
  8. end;  

说明:

table_type pl/sql表类型

emp.ename%type 指定了表的元素的类型和长度

stable pl/sql表变量

stable(0) 则表示下标为0的元素


参照变量

参照变量是指用于存放数值指针的变量,通过使用参照变量,可以使得应用程序共享相同对象,从而降低占用的空间。在编写pl/sql程序时,可以使用游标变量(ref cursor)对象类型变量(ref obj_type)两种参照变量类型。

 

游标变量

通过游标可以取得返回结果集(这个结果集,往往是select语句的结果)的任何一行数据,从而提供共享的效率。

 

游标(ref cursor)使用

定义游标基本语法:

type 自定义游标名 is ref cursor;

变量名 自定义游标名;

 

打开游标基本语法:

open 游标变量 for select 语句;

 

取出当前游标指向的行基本语法:

fetch 游标变量 into 其它变量;

 

判断游标是否指向记录最后基本语法:

游标变量%notfound

 

使用游标时,当定义游标时不需要指定相应的select语句,但是当使用游标时,(open时)需要指定select语句,这样一个游标就写一个select语句结合了

[sql] view plain copy
  1. --参照变量  
  2. --编写过程,输入部门号,显示该部门所有员工的姓名和工资  
  3. create or replace procedure p8(v_in_deptno in dept2.deptno%type) is  
  4. --定义游标变量类型  
  5. type emp_cursor is ref cursor;  
  6. --定义游标变量  
  7. v_emp_cursor emp_cursor;  
  8. v_ename emp2.ename%type;  
  9. v_sal emp2.sal%type;  
  10. begin  
  11.   --打开游标  
  12.   open v_emp_cursor for select ename,sal from emp2 where deptno = v_in_deptno;  
  13.   --用循环语句取出游标中指向的每行数据  
  14.   loop  
  15.     --取出每行数据  
  16.     fetch v_emp_cursor into v_ename,v_sal;  
  17.     --判断游标退出  
  18.     exit when v_emp_cursor%notfound;  
  19.     --输出每行数据  
  20.     dbms_output.put_line('员工的姓名为:' || v_ename || ' 员工的工资为:' || v_sal);  
  21.   end loop;  
  22.   --关闭游标  
  23.   close v_emp_cursor;  
  24. end;  
  25. /  

[sql] view plain copy
  1. --参照变量  
  2. --编写过程,输入部门号,显示该部门所有员工的姓名和工资并且如果某个员工的工资低于2000则增加100  
  3. create or replace procedure p9(v_in_deptno in dept2.deptno%type) is  
  4. --定义游标变量类型  
  5. type emp_cursor is ref cursor;  
  6. --定义游标变量  
  7. v_emp_cursor emp_cursor;  
  8. v_ename emp2.ename%type;  
  9. v_sal emp2.sal%type;  
  10. v_empno emp2.empno%type;  
  11. begin  
  12.   --打开游标  
  13.   open v_emp_cursor for select ename,sal,empno from emp2 where deptno = v_in_deptno;  
  14.   --用循环语句取出游标中指向的每行数据  
  15.   loop  
  16.     --取出每行数据  
  17.     fetch v_emp_cursor into v_ename,v_sal,v_empno;  
  18.     --判断游标退出  
  19.     exit when v_emp_cursor%notfound;  
  20.     --如果某个员工的工资低于2000则增加100  
  21.     if(v_sal < 2000) then  
  22.              update emp2 set sal = sal + 100 where empno = v_empno;  
  23.     end if;  
  24.     --输出每行数据  
  25.     dbms_output.put_line('员工的姓名为:' || v_ename || ' 员工的工资为:' || v_sal);  
  26.   end loop;  
  27.   --关闭游标  
  28.   close v_emp_cursor;  
  29. end;  

0 0
原创粉丝点击