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