关于oracle中PL/SQL语句,即存储过程的写法和调用。

1.什么是PL/SQL,有什么作用
  --Procedure Language/SQL 过程语言+SQL
  --PL/SQL是Oracle数据库内部存储和运行
    一段程序
  --PL/SQL是Oracle在SQL语句基础上扩展了
   一些判断,循环等编程元素.可以实现一些
   复杂的运算和逻辑功能.
  --PL/SQL适合大量数据运算.


2.PL/SQL基本结构
   DECLARE
     ....//变量声明区
   BEGIN
     ....//主要处理逻辑;循环,判断+SQL
   EXCEPTION  
     ....//异常处理逻辑
   END;//结束


  set serveroutput on;//打开oracle控制台输出
  //最简单的pl/sql
  begin
     dbms_output.put_line('Hello PL/SQL');
  end;
  注意:pl/sql语句块是以"/"作为结束运行标识.
上面结构的PL/SQL程序段,只编译运行一次,
不会在Oracle存储,如果需要存储可以写成
存储过程,触发器等元素.


3.PL/SQL基本语法
   1)注释
      -- 单行注释内容
      /*
         多行注释内容
      */
   2)变量定义和赋值操作
      变量名 类型;
      a number(5);//a为null
      b number(3) := 100;//b为100


    注意:变量定义,如果没有指定值,默认为null.
赋值语句为:=格式. =号用于判断比较是否相等.


    declare
        a number(5) := 100; --定义变量a赋值100
        b number(5) :=200; --定义变量a赋值300
        c number(5);
    begin
        c := a+b; --将a+b结果给c赋值
        dbms_output.put_line('结果是:' || c);
    end;


   3)if判断
     if 条件 then
        ...
     elsif 条件 then
        ...
     else
        ...
     end if;


    ------------------
    declare
       sex varchar2(2) := 'F';
    begin
       if sex='M' then
          dbms_output.put_line('Male');
       elsif sex='F' then
          dbms_output.put_line('Female');
       else
           dbms_output.put_line('Yao!!!');
       end if;
    end;
    //有a,b,c三个number(3)变量,如果a>b,c=1;
如果a=b,c=0;如果a    declare
      a number(3) :=300;
      b number(3) :=200;
      c number(3);
   begin
       if a>b then
          c:=1;
      elsif a=b then
          c:=0;
      else
          c:=-1;
      end if;
      dbms_output.put_line('a,b比较结果:' || c);
   end;


  4)循环
    提供了3种循环语法;
   a.loop循环
    declare
       i number(2) :=1;
    begin
       loop   --循环开始标记
          dbms_output.put_line(i);
          i:=i+1;
          exit when i>10; --指定结束循环条件
       end loop; --循环结束标记
    end;
   ---------------------
      declare
          i number(3) :=1;
          s number(11) := 0;
              --合计变量,指定初值0,否则为null,
              --null和其他数累加结果还是null
      begin
          loop
             s := s+i;
             i:=i+1;
             exit when i>100;
          end loop;
          dbms_output.put_line(s);
      end;
   
    b.while循环
      declare
         i number(3):=1;
      begin
         while i<=10 loop --满足条件进入循环;不满足退出循环
            dbms_output.put_line(i);
            i:=i+1;
         end loop;--循环结束标记
      end;
   
    c.for循环
     begin
        for i in 1..10 loop --循环1到10输出
           dbms_output.put_line(i);
        end loop;--循环结束标记
     end;
   FOR循环使用注意事项:
-- 循环变量i不需要定义,会根据集合元素自动定义
-- in 后面的集合只适用于数值范围
  5)PL/SQL使用DML和TCL事务语句
   DML和TCL语句可以直接在PL/SQL程序中
使用,没有任何特殊要求.
   begin
      --向dept表插入一条记录
      insert into dept(deptno,dname,loc) 
values (50,'testing','beijing');
      --删除编号为60的部门 
      delete from dept where deptno=60;
      --提交事务
      commit;
   end;
   6)PL/SQL使用DDL语句
     先前学习的create tabe...,drop table,alter table
语句必须采用下面格式:
     execute immediate 'DDL语句'; --立即执行‘DDL语句’。
------------------
    begin
        execute immediate 'create table emp_bak (EMPNO number(11),ENAME varchar2(20),SAL number(9,2))';
    end;
-----------------------
   begin
       --建表
       execute immediate 
              'create table c001 (c1 number(5))';
       --插入记录
       insert into c001 (c1) values (5);
       commit;--提交事务
   end;
  上面PL/SQL编译会发生错误,因为编译insert
语句时,找不到c001表.
   execute immediate 'ddl语句';在编译时将dll语句
当作普通的字符串.执行时将字符串当作sql指令执行
-----------------------------
   begin
       --建表
       execute immediate 
              'create table c001 (c1 number(5))';
       --插入记录
       execute immediate 
             'insert into c001 (c1) values (5)';
       commit;--提交事务
   end;


     execute immediate
        'insert into c002 (c1) values ('abc')';
   使用:chr()函数,chr(39)将ascii=39字符返回.  
    execute immediate
        'insert into c002 (c1) values ('||chr(39)||'abc'||chr(39)||')';
--------------------------------
   7)PL/SQL使用DQL查询操作
     a)有且只有一条结果(使用select语句)
      使用select语句必须按照下面结构使用
       select 字段... into 变量...
       from 表 where 条件;
      上面语句可以将查询出的字段值给变量.
   declare
      --v_deptno number(2);
      --v_dname varchar2(14);
      v_deptno dept.deptno%TYPE;
      v_dname dept.dname%TYPE;
   begin
       --查询将结果给变量
       select deptno,dname into v_deptno,v_dname
       from dept where deptno=30;
       --显示变量值
       dbms_output.put_line(v_deptno||v_dname);
   end;


    使用%TYPE定义变量.格式如下:
--v_deptno类型大小与dept表的deptno字段一致
v_deptno dept.deptno%TYPE;
--v_dname类型大小与dept表的dname字段一致
v_dname dept.dname%TYPE;




   b)如果返回0或多个结果(使用CURSOR游标)
   CURSOR使用步骤:
  --在declare定义游标
    CURSOR 游标名 IS 原有select语句;
  --在begin中使用
    open 游标名;//打开游标,执行select语句.
    循环获取数据进行运算;
    fetch 游标名 into 变量;
    close 游标名;    
========================
declare
   cursor c_dept_cursor is 
      select deptno,dname from dept order by deptno;
   v_deptno dept.deptno%type;
   v_dname dept.dname%type;
begin
   open c_dept_cursor;--打开游标
   loop
       --向下移动指针,抓取记录
       fetch c_dept_cursor into v_deptno,v_dname;
       --判断是否该结束循环
       exit when c_dept_cursor%NOTFOUND;
        --打印变量值
        dbms_output.put_line(v_deptno||'  '||v_dname);
   end loop;
    close c_dept_cursor;--关闭游标
end;
=========游标变量数据过程===========
--游标初始状态,指针指向第一行记录上方
--当使用fetch抓取记录时,会向下移动游标指针,
   然后将指针指向的记录取出
--当指针指向最后一条记录时,再执行fetch,
指针会停留在最后一行,但是会将%NOTFOUND
属性值改为true;
=================================
declare
  cursor c_dept_cursor is 
    select deptno,dname from dept order by deptno;
  v_deptno dept.deptno%type;
  v_dname dept.dname%type;
begin
  open c_dept_cursor;
  --初始指针指向第一行记录上方,没有记录
  --因此需要fetch向下移动指针
  fetch c_dept_cursor into v_deptno,v_dname;
  while c_dept_cursor%FOUND loop
      dbms_output.put_line(v_deptno||' '||v_dname);
      fetch c_dept_cursor into v_deptno,v_dname;
  end loop;
  close c_dept_cursor;
end;


=========%ROWTYPE============
v_dept dept%ROWTYPE;--记录类型变量
v_dept是一个记录类型,其中有多个字段,
字段个数,名称,类型,大小与dept表一致.
它可以存储dept中的一行记录.
利用"v_dept.字段名"可以访问记录数据.


declare
   cursor c_dept_cursor is
     select * from dept order by deptno;
   --v_dept变量和dept字段数量,类型,大小一致
   v_dept dept%ROWTYPE;--记录类型
begin
   open c_dept_cursor;
   loop
      fetch c_dept_cursor into v_dept;
      exit when c_dept_cursor%NOTFOUND;
      dbms_output.put_line(
            v_dept.deptno||' '||v_dept.dname);
   end loop;
   close c_dept_cursor;

end;

=====================================================================

1.plsql基础
declare
   变量名 类型; --默认为null
   变量名 类型 := 值;


:=和=区别
/的作用
--和/* */注释
匿名块plsql
declare ... begin ... exception ... end;
DML语句与原SQL相同
SELECT语句需要采用select ... into ...,
(select结果有且只有一条记录)
CURSOR查询多行或0行记录
declare
  CURSOR v_emp_cursor IS select .....
begin
  //打开游标Open   
  //利用LOOP循环遍历fetch
  //关闭游标Close
end;
DDL语句需要使用execute immediate 'DDL语句';
chr(39)对应'
ascii('a')对应97
使用两个''表示一个'
if...then
 ...
elsif ... then
 ...
else
 ...
end if;
loop
   exit when ...
end loop;


while ... loop


end loop;


for 变量 in 起点...终点 loop


end loop;


for 变量 in ascii('a')...ascii('z') loop


end loop;
====================
PL/SQL中匿名块不能存储在Oracle,如果需要
存储在Oracle上,需要定义成存储过程,函数,
触发器,包等对象元素.
1.存储过程
CREATE [OR REPLACE]  PROCEDURE 过程名
[(参数 IN | OUT | IN OUT 类型,参数 类型...)]
IS|AS
  --原declare声明区
BEGIN
  --主处理区
EXCEPTION
  --异常处理区
END;
  -----示例1------
 create or replace procedure helloworld
 is
  v_count number(11);
 begin
   --统计dept数量显示
   select count(*) into v_count from dept;
   dbms_output.put_line('dept toal:' || v_count);
 end;
----------调用----------
 set serveroutput on;--先打开控制台显示
 exec helloworld();或 call helloworld();或匿名块


------示例2--------
根据传入的empno显示名字和工资(工资+奖金)
--参数类型不要指定大小,名字不要与字段冲突
create or replace procedure show_emp
(no in number) 
is
 v_ename emp.ename%TYPE;
 v_sal emp.sal%TYPE;
begin
   select ename,sal+NVL(comm,0) 
   into v_ename,v_sal 
   from emp 
   where empno=no;
   dbms_output.put_line(v_ename || '  ' || v_sal);
end;


------示例3-------
传入两个整数,传出合计结果和差值结果
create or replace procedure sumsub
(v_i in number,v_j in number,
v_sum out number,v_sub out number)
is
begin
    v_sum := v_i+v_j;    --求和
    v_sub := v_i-v_j;      --求差
end;
--------测试-----------
declare
v_sum number;
v_sub number;
begin
--调用存储过程,in参数给常量,out参数给变量
sumsub(10,50,v_sum,v_sub);
dbms_output.put_line('sum:'||v_sum);
dbms_output.put_line('sub:'||v_sub);
end;
/
---------综合练习-------------
基于EMP表,根据EMP员工记录和工资信息
利用PL/SQL语句向EMP_TAX员工纳税记录表
生成数据信息.
EMP_TAX表结构如下:
EMPNO (员工编号)
ENAME (员工名)
SALARAY(工资+奖金) 
TAX  (纳税金额)
TAX_DATE(纳税日期-系统日期)
create table emp_tax(
empno number(4) ,
ename varchar2(10),
salary number(7,2),
tax number(7,2),
tax_date date);
-----------扣税规则--------------
工资+奖金=总金额按一下规则计算
2000(包含)以下不扣税,2000为免税基数
2000-3000之间的超出部分按5%扣税
3000(包含)以上超出部分按10%扣税


2500-->500*5%
3500-->1000*5%+500*10%


declare
  CURSOR c_emp_cursor IS
    select * from emp; --定义游标
  v_emp emp%ROWTYPE; --存储一行游标记录
  v_salary number(7,2) :=0.0 ;  --存储工资合计
  v_tax number(7,2) :=0.0; --存储缴税金额
begin
   open c_emp_cursor;
   loop --循环抓取员工记录
       fetch c_emp_cursor into v_emp;
         exit when c_emp_cursor%NOTFOUND;
       --计算该员工工资合计
       v_salary := v_emp.sal + NVL(v_emp.comm,0);
       --计算应缴税金额
       if v_salary <= 2000 then
          v_tax := 0.0;
       elsif v_salary <3000 then
          v_tax := (v_salary-2000)*0.05;
       else
           v_tax := (v_salary-3000)*0.1+1000*0.05;
       end if;
       --将该员工缴税信息插入emp_tax表
       insert into emp_tax 
           (empno,ename,salary,tax,tax_date)
       values (v_emp.empno,v_emp.ename,v_salary,v_tax,sysdate);
   end loop;
   commit; --提交事务
   close c_emp_cursor;
end;


---------将上述匿名块改成存储过程---------
将declare关键字改为
create or replace procedure emp_tax_pro
is
  CURSOR c_emp_cursor IS
    select * from emp; --定义游标
  v_emp emp%ROWTYPE; --存储一行游标记录
  v_salary number(7,2) :=0.0 ;  --存储工资合计
  v_tax number(7,2) :=0.0; --存储缴税金额
begin
   open c_emp_cursor;
   loop --循环抓取员工记录
       fetch c_emp_cursor into v_emp;
         exit when c_emp_cursor%NOTFOUND;
       --计算该员工工资合计
       v_salary := v_emp.sal + NVL(v_emp.comm,0);
       --计算应缴税金额
       if v_salary <= 2000 then
          v_tax := 0.0;
       elsif v_salary <3000 then
          v_tax := (v_salary-2000)*0.05;
       else
           v_tax := (v_salary-3000)*0.1+1000*0.05;
       end if;
       --将该员工缴税信息插入emp_tax表
       insert into emp_tax1 
           (empno,ename,salary,tax,tax_date)
       values (v_emp.empno,v_emp.ename,v_salary,v_tax,sysdate);
   end loop;
   commit; --提交事务
   close c_emp_cursor;
end;


2.函数
  数据库本身提供了很多函数,例如字符串处理,
数值处理,日期函数等.当Oracle提供的不能满足
需求时,可以自定义函数.
   CREATE [OR REPLACE] FUNCTION 函数名
   [(参数 类型,参数 类型)]
   RETURN 返回类型
   IS或AS
   PL/SQL语句块
 注意:函数必须得有返回值.参数都是IN传入模式
----------示例1------------
sign(n):Oracle提供的判断n值的符号(>0,<0,=0).
自定义一个mysign,功能与sign一致.
create or replace function mysign(n number)
return number    --定义返回类型
is
v_f number := 0;
begin
   --根据n参数变量值,判断
   if n>0 then
      v_f := 1;
   elsif n=0 then
      v_f := 0;
   else
       v_f := -1;
   end if;
   return v_f;
end;


--------示例2----------
根据指定的empno值返回工资合计值
create or replace function get_sal
(v_empno emp.empno%TYPE)
return number
is
v_total_sal number(7,2) :=0.0; --存储工资合计
begin
 select sal+NVL(comm,0) into v_total_sal
 from emp where empno=v_empno;
 return v_total_sal; --返回合计结果
end;
------------删除过程和函数--------------
drop function xxx; --删除函数
drop procedure xxx; --删除存储过程
-------------示例3--------------------
自定义函数,传入工资合计值,返回应缴税额.
纳税逻辑参考存储过程部分的综合练习.
create or replace function get_tax
(v_salary number) return number
is
v_tax number(7,2) :=0.0; --存储应纳税额
begin
       if v_salary <= 2000 then
          v_tax := 0.0;
       elsif v_salary <3000 then
          v_tax := (v_salary-2000)*0.05;
       else
           v_tax := (v_salary-3000)*0.1+1000*0.05;
       end if;
       return v_tax;
end;
--------基于get_tax函数编写纳税存储过程------------
create or replace procedure emp_tax_pro
is
  CURSOR c_emp_cursor IS
    select * from emp; --定义游标
  v_emp emp%ROWTYPE; --存储一行游标记录
  v_salary number(7,2) :=0.0 ;  --存储工资合计
  v_tax number(7,2) :=0.0; --存储缴税金额
begin
   open c_emp_cursor;
   loop --循环抓取员工记录
       fetch c_emp_cursor into v_emp;
         exit when c_emp_cursor%NOTFOUND;
       --计算该员工工资合计
       v_salary := v_emp.sal + NVL(v_emp.comm,0);
       --计算应缴税金额
      v_tax := get_tax(v_salary);
       --将该员工缴税信息插入emp_tax表
       insert into emp_tax 
           (empno,ename,salary,tax,tax_date)
       values (v_emp.empno,v_emp.ename,v_salary,v_tax,sysdate);
   end loop;
   commit; --提交事务
   close c_emp_cursor;
end;


3.触发器
   1)触发器分类
 DML触发器:由insert,update,delete动作触发调用
 系统触发器:由登录,登出Oracle系统事件触发调用
 2)DML触发器的使用
    a.语句级触发器
 一个insert或update或delete语句自动触发一次.
  --------示例1----------
每次执行emp表的insert,update,delete操作,
都会自动统计出emp中员工总人数,
平均工资数,工资合计信息
create or replace trigger  emp_trigger1
after insert or update or delete on emp
declare
   v_count number(7) := 0; --存储员工人数
   v_avg number(7,2) := 0.0; --存储平均工资
   v_sum number(11,2) :=0.0; --存储工资总额
begin
   select count(*),avg(sal),sum(sal)
   into v_count,v_avg,v_sum
   from emp;
   dbms_output.put_line('员工人数' || v_count);
   dbms_output.put_line('平均工资' || v_avg);
   dbms_output.put_line('工资总额' || v_sum);
end;




    *b.行级触发器
 insert,update,delete语句影响的每一行记录都要
自动触发一次.
可以在触发器中获取操作影响的记录信息.
在行级触发器中,可以使用下面两个关键字
:NEW 表示执行SQL之后的记录状态
:OLD 表示执行SQL之前的记录状态


insert操作只能使用:NEW,表示要插入的记录
delete操作只能使用:OLD,表示要删除之前的记录
update操作可以使用:NEW和:OLD, :NEW表示
更新之后的记录;:OLD表示更新之前的记录
----------示例1------------
将emp表删除的记录,存入emp_bak备份表.
create or replace trigger emp_trigger2
before delete on emp for each row
declare 
begin
    --将要删除的记录empno,ename,sal写入emp_bak
    --OLD表示即将要删除的EMP记录
    insert into emp_bak(empno,ename,sal)
       values (:OLD.empno,:OLD.ename,:OLD.sal);
end;
注意:触发器中不要使用事务提交操作.


4.异常处理
begin
 ....
exception
 ....//异常处理区
end;
------------------
exception
  when  异常类型 then
      .......
  when 异常类型 then
      .......
  when others then
      .......
--------------------
1)异常分类
  a.预定义异常
    在Oracle内部,提前将错误编码和特定的
异常类型名关联.由Oracle自动触发该异常.
    exception
       when no_data_found then
           dbms_output.put_line('没有记录');
  -------示例----------
  declare
      v_sal number(7,2);
   begin
       select sal into v_sal from emp 
       where empno=7654;
       dbms_output.put_line(v_sal);
   exception
       when too_many_rows then
           dbms_output.put_line('返回记录太多啦');
       when others then
           dbms_output.put_line('出错啦,原因自己找吧');
   end;




  b.非预定义异常
    需要在程序中将错误编码和定义的异常类型
关联.由Oracle自动触发该异常
    myexception exception;--定义类型名
    PRAGMA EXCEPTION_INIT
(myexception,-2292);
   ----------------------
    exception 
        when myexception then
             dbms_output.put_line('错误')
  c.自定义异常
    没有错误编号,违反业务需求时,程序员自定义
一个异常类型,在程序中使用raise 异常类型抛出.
 由程序定义和触发.
      no_update_exception exception;
   begin
      ......
      raise no_update_exception;//抛出异常
      .....
   exception
      when no_update_exception then
         dbms_output.put_line('没有更新记录');
   end;


5.Java调用存储过程

package org.tarena;

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.Types;

import org.tarena.util.DbUtil;

public class TestCallPro {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
//		testPro1();
		testPro2();
	}
	
	public static void testPro1(){
		Connection con = null;
		CallableStatement callStat = null;
		try{
			con = DbUtil.openConnection();
		//获取执行存储过程的Statement
			//指定命令格式{call 存储过程名()}
			callStat = con.prepareCall("{call emp_tax_pro()}");
			callStat.execute();
		}catch(Exception ex){
			ex.printStackTrace();
		}finally{
			DbUtil.closeConnection(con,callStat);
		}
	}
	
	public static void testPro2(){
		Connection con = null;
		CallableStatement callStat = null;
		try{
			con = DbUtil.openConnection();
		//获取执行存储过程的Statement
			//指定命令格式{call 存储过程名()}
			callStat = 
				con.prepareCall("{call sumsub(?,?,?,?)}");
			callStat.setInt(1, 10);//指定传入值
			callStat.setInt(2, 60);//指定传入值
			callStat.registerOutParameter(
					3, Types.INTEGER);//定义传出的integer类型
			callStat.registerOutParameter(
					4, Types.INTEGER);//定义传出的integer类型
			callStat.execute();
			//获取Out参数返回值
			int sum = callStat.getInt(3);//获取第3个问号值
			int sub = callStat.getInt(4);//获取第4个问号值
			System.out.println("合计结果"+sum);
			System.out.println("差值结果"+sub);
		}catch(Exception ex){
			ex.printStackTrace();
		}finally{
			DbUtil.closeConnection(con,callStat);
		}
	}

}


你可能感兴趣的:(oralce)