Oracle编程
一、PL/SQL
1、PL/SQL概述
PL/SQL(Procedure Language/SQL)是 Oracle 对 sql 语言的过程化扩展,使 SQL 语言具有过程处理能力。
基本语法结构
[declare -- 声明变量
]begin-- 代码逻辑 [exception-- 异常处理
]end;
2、变量
1)变量的声明与赋值
-- 声明变量
变量名 类型(长度);-- 变量赋值
变量名:=变量值;
2)直接赋值
声明变量水费单价、水费字数、吨数、金额。
对水费单价、字数、进行赋值 。吨数根据水费字数换算,规则为水费字数除以
1000,并且四舍五入,保留两位小数。计算金额,金额 = 单价 * 吨数。
输出:单价、数量和金额。
declarev_price number(10,2); -- 水费单价v_usenum number; -- 水费字数v_usenum2 number(10,2); -- 吨数v_money number(10,2); -- 金额
begin-- 变量赋值v_price:=2.45;v_usenum:=8012;-- 字数换算为吨数v_usenum2:= round( v_usenum/1000,2 );-- 计算金额v_money:=round(v_price*v_usenum2,2);-- 文字的输出dbms_output.put_line('金额:'||v_money);
end;
2)select into 赋值
-- select into 语法
select 列名 into 变量名 from 表名 where 条件
select into
结果必须是一条记录 ,有多条记录和没有记录都会报错
declarev_price number(10,2); -- 单价v_usenum number; -- 水费字数v_num0 number; -- 上月字数v_num1 number; -- 本月字数v_usenum2 number(10,2); -- 使用吨数v_money number(10,2); -- 水费金额
begin-- 对单价进行赋值v_price:=3.45;-- select into赋值select usenum,num0,num1 into v_usenum,V_num0,V_num1 from T_ACCOUNT where year='2012' and month='01' and owneruuid=1;-- 字数换算v_usenum2:= round(v_usenum/1000,2);-- 计算金额v_money:=v_price*v_usenum2;-- 文字的输出DBMS_OUTPUT.put_line(''金额:'||v_money||'上月字数:'||v_num0||'本月字数'||v_num1);
end;
3、属性类型
1)%TYPE 引用型
引用某表某列的字段类型
对于不知道表内类型的数据,可以直接获取表内列的类型
declarev_price number(10,2); -- 单价v_usenum T_ACCOUNT.USENUM%TYPE; -- 水费字数v_num0 T_ACCOUNT.NUM0%TYPE; -- 上月字数v_num1 T_ACCOUNT.NUM1%TYPE; -- 本月字数v_usenum2 number(10,2); -- 使用吨数v_money number(10,2); -- 水费金额
2)%ROWTYPE 记录型
标识某个表的行记录类型
一个记录型代表一行数据,类似java编程的实体类
declarev_price number(10,2); -- 单价v_account T_ACCOUNT%ROWTYPE; -- 台账行的记录型v_usenum2 number(10,2); -- 使用吨数v_money number(10,2); -- 水费金额
begin-- 对单价进行赋值v_price:=3.45;-- select into赋值select * into v_account from T_ACCOUNT where year='2012' and month='01' and owneruuid=1;-- 使用吨数v_usenum2:= round(v_account.usenum/1000,2);-- 计算金额v_money:=v_price*v_usenum2;-- 文字的输出 DBMS_OUTPUT.put_line('金额:'||v_money);
end;
4、异常(例外)
发生异常后,语句将停止执行,控制权转移到 PL/SQL 块的异常处理部分
- 预定义异常:Oracle预先定义的异常
NO_DATA_FOUND
:使用 select into 未返回行TOO_MANY_ROWS
:执行 select into 时,结果集超过一行
- 用户定义异常:用户定义异常,通过
RAISE
语句显式引发
-- 异常语法结构exceptionwhen 异常类型 then异常处理逻辑
declarev_price number(10,2); -- 水费单价v_usenum T_ACCOUNT.USENUM%type; -- 水费字数v_usenum2 number(10,3); -- 吨数v_money number(10,2); -- 金额 begin-- 水费单价v_price:=2.45;-- select into赋值select usenum into v_usenum from T_ACCOUNTwhere owneruuid=1 and year='2012' and month='01';-- 字数换算为吨数v_usenum2:= round( v_usenum/1000,3);-- 计算金额v_money:=round(v_price*v_usenum2,2);-- 信息输出dbms_output.put_line('金额:'||v_money);-- 异常处理
exceptionwhen NO_DATA_FOUND thendbms_output.put_line('未找到数据');when TOO_MANY_ROWS thendbms_output.put_line('查询条件有误,返回多条信息');
end;
5、条件判断
-- 1、if
if 条件 then代码;
end if;-- 2、if else
if 条件 then代码;
else代码;
end if;-- 3、if elif eles
if 条件 then代码;
elsif 条件 then代码;
else代码;
end if;
设置三个等级的水费。 5 吨以下 2.45 元/吨,5 吨到 10 吨部分 3.45 元/吨,超过 10 吨部分 4.45 元/吨
根据使用水费的量来计算阶梯水费。
declarev_price1 number(10,2); -- 不足 5 吨的单价v_price2 number(10,2); -- 超过 5 吨不足 10 吨单价v_price3 number(10,2); -- 超过 10 吨单价v_account T_ACCOUNT%ROWTYPE;-- 记录型v_usenum2 number(10,2); -- 使用吨数v_money number(10,2); -- 水费金额
begin-- 单价赋值v_price1:=2.45;v_price2:=3.45;v_price3:=4.45;-- select into赋值select * into v_account from T_ACCOUNT where year='2012' and month='01' and owneruuid=1;-- 使用吨数v_usenum2:= round(v_account.usenum/1000,2);-- 计算金额(阶梯水费)-- 第一个阶梯if v_usenum2<=5 thenv_money:=v_price1*v_usenum2;-- 第二个阶梯elsif v_usenum2>5 and v_usenum2<=10 then v_money:=v_price1*5+v_price2*(v_usenum2-5); -- 第三个阶梯elsev_money:=v_price1*5+v_price2*5+v_price3*(v_usenum2-10);end if;-- 信息输出DBMS_OUTPUT.put_line('金额:'||v_money);-- 异常处理
exceptionwhen NO_DATA_FOUND thenDBMS_OUTPUT.put_line('没有找到数据');when TOO_MANY_ROWS thenDBMS_OUTPUT.put_line('返回的数据有多行');
end;
6、循环
1)loop无条件循环
loop代码exit when 退出条件;
end loop;
-- 输出1至100
declarev_num number:=1;
begin loopdbms_output.put_line(v_num);v_num:=v_num+1;exit when v_num>100;end loop;
end;
2)while条件循环
while 条件
loop代码
end loop;
-- 输出1至100
declarev_num number:=1;
begin while v_num<=100loopdbms_output.put_line(v_num);v_num:=v_num+1;end loop;
end;
3)for循环
for 变量 in 起始值 .. 终止值
loop代码
end loop;
-- 输出1至100
beginfor v_num in 1 .. 100loopdbms_output.put_line(v_num); end loop;
end;
7、游标
1)游标概述
存放 SQL 语句执行的结果集
2)游标的语法
-- 声明游标
cursor 游标名称 is SQL语句;-- 使用游标
open 游标名称
loopfetch 游标名称 into 变量;exit when 游标名称%notfound;
end loop;
close 游标名称
打印业主类型为 1 的价格表
declare-- 价格的行对象v_pricetable T_PRICETABLE%rowtype;-- 定义游标cursor cur_pricetable is select * from T_PRICETABLE where ownertypeid=1;
begin-- 打开游标open cur_pricetable;loop-- 提取游标到变量fetch cur_pricetable into v_pricetable;-- 当游标到最后一行下面退出循环exit when cur_pricetable%notfound;-- 打印数据dbms_output.put_line('价格:'||v_pricetable.price); end loop;-- 关闭游标close cur_pricetable;
end ;
3)带参数的游标
条件值有可能是在运行时才能决定的
类似于java的传参
declare-- 价格的行对象v_pricetable T_PRICETABLE%rowtype;-- 定义游标cursor cur_pricetable(v_ownertype number) is select * from T_PRICETABLE where ownertypeid=v_ownertype;
begin-- 打开游标open cur_pricetable(1);loop-- 提取游标到变量fetch cur_pricetable into v_pricetable;-- 当游标到最后一行下面退出循环exit when cur_pricetable%notfound;-- 打印数据dbms_output.put_line('价格:'||v_pricetable.price); end loop;-- 关闭游标close cur_pricetable;
end ;
4)for 循环提取游标值
declare-- 定义游标cursor cur_pricetable(v_ownertypeid number) is select * from T_PRICETABLE where ownertypeid=v_ownertypeid;
begin-- for循环for v_pricetable in cur_pricetable(3)loop dbms_output.put_line('价格:'||v_pricetable.price); end loop;
end ;
二、存储函数
1、存储函数概述
存储函数又称为自定义函数。可以接收一个或多个参数,返回一个结果。
2、存储函数语法结构
create [ or replace ] function 函数名称
(参数名称 参数类型, 参数名称 参数类型, ...)
return 结果变量数据类型
is变量声明部分;
begin逻辑部分;return 结果变量;
[exception 异常处理部分]
end;
3、案例
创建存储函数,根据地址 ID 查询地址名称
create function fn_getaddress(v_id number)
return varchar2
isv_name varchar2(30);
beginselect name into v_name from t_address where id=v_id;return v_name;
end;
-- 测试函数
select fn_getaddress(3) from dual-- 函数在子查询的应用
select id 编号,name 业主名称,fn_getaddress(addressid) 地址 from t_owners
三、存储过程
1、存储过程概述
存储过程没有return
,但是可以通过传出函数,传出多个返回值
应用程序可以调用存储过程,执行相应的逻辑,对业务逻辑的封装。
与MVC框架的思想冲突
效率比MVC框架高
2、存储过程语法结构
create [ or replace ] procedure 存储过程名称
(参数名 类型, 参数名 类型, 参数名 类型)
is|as变量声明部分;
begin逻辑部分;
[exception 异常处理部分;]
end;
参数只指定类型,不指定长度
过程参数的三种模式:
IN
:传入参数(默认)OUT
:传出参数 ,主要用于返回程序运行结果IN OUT
:传入传出参数
3、案例
1)不带传出参数的存储过程
-- 创建
create or replace procedure pro_owners_add
(v_name varchar2,v_addressid number,v_housenumber varchar2, v_watermeter varchar2,v_type number
)
is
begininsert into T_OWNERS values( seq_owners.nextval,v_name,v_addressid,v_housenumber,v_watermeter,sysdate,v_type);commit;
end;-- 调用
-- 1、call
call pro_owners_add('赵伟',1,'999-3','132-7',1);
-- 2、begin end
beginpro_owners_add('赵伟',1,'999-3','132-7',1);
end;
// JDBC 调用存储过程
public static void add(Owners owners){java.sql.Connection conn = null;java.sql.CallableStatement stmt = null;try {conn = BaseDao.getConnection();stmt = conn.prepareCall("{call pro_owners_add(?,?,?,?,?)}");stmt.setString(1, owners.getName());stmt.setLong(2, owners.getAddressid());stmt.setString(3, owners.getHousenumber());stmt.setString(4, owners.getWatermeter());stmt.setLong(5, owners.getOwnertypeid());stmt.execute();} catch (SQLException e) {e.printStackTrace();} finally {BaseDao.closeAll(null, stmt, conn);}
}
2)带传出参数的存储过程
-- 创建
create or replace procedure pro_owners_add
(v_name varchar2,v_addressid number,v_housenumber varchar2, v_watermeter varchar2,v_type number,v_id out number -- 传出参数
)
is
beginselect seq_owners.nextval into v_id from dual;insert into T_OWNERS values(v_id,v_name,v_addressid,v_housenumber,v_watermeter,sysdate,v_type);commit;
end;-- 调用
declarev_id number;
beginpro_owners_add('王旺旺',1,'922-3','133-7',1,v_id);DBMS_OUTPUT.put_line('增加成功,ID:'||v_id);
end;
// JDBC 调用存储过程
public static long add(Owners owners) {long id = 0;java.sql.Connection conn = null;java.sql.CallableStatement stmt = null;try {conn = BaseDao.getConnection();stmt = conn.prepareCall("{call pro_owners_add(?, ?,?,?,?,?)} ");stmt.setString(1, owners.getName());stmt.setLong(2, owners.getAddressid());stmt.setString(3, owners.getHousenumber());stmt.setString(4, owners.getWatermeter());stmt.setLong(5, owners.getOwnertypeid());// 册传出参数类型:表明第六个参数是传出参数stmt.registerOutParameter(6, OracleTypes.NUMBER);// 执行stmt.execute();// 执行后传出参数id = stmt.getLong(6);} catch (SQLException e) {e.printStackTrace();} finally {BaseDao.closeAll(null, stmt, conn);}return id;
}
四、触发器
1、触发器概述
触发器是一个与表相关联的、存储的PL/SQL程序。
每当一个特定的操作,Oracle自动地执行触发器中定义的语句序列。
触发器可用于
- 数据确认:录入的合法性验证
- 实施复杂的安全性检查
- 做审计,跟踪表上所做的数据操作等
- 数据的备份和同步
触发器分类
- 前置触发器(BEFORE):sql执行前执行触发器,可以修改sql执行的值
- 后置触发器(AFTER):sql执行后执行触发器
2、创建触发器
create [or replace] trigger 触发器名before|after[delete][[or] insert] [[or] update[of 列名]]on 表名[for each row][when(条件)]
declare变量
begin代码
end;
FOR EACH ROW
:作用是标注此触发器是行级触发器,不标注为语句级触发器- 行级触发器:每影响一行触发一次
- 语句级触发器:每个语句只触发一次
在触发器中触发语句与伪记录变量的值
触发语句 | :old(修改前的行数据) | :new(修改后的行数据) |
---|---|---|
insert | 所有字段都是空(null) | 将要插入的数据 |
update | 更新以前该行的值 | 更新后的值 |
delete | 删除以前该行的值 | 所有字段都是空(null) |
3、案例
1)前置触发器
当用户输入本月累计表数后,自动计算出本月使用数
CREATE OR REPLACE TRIGGER tri_account_update_num1before UPDATE OF num1 ON t_account FOR each ROW
DECLARE
BEGIN: new.usenum :=: new.num1 -: new.num0;
END;
2)后置触发器
当用户修改了业主信息表的数据时记录修改前与修改后的值
CREATE TRIGGER tri_owners_logafterUPDATE OF nameON t_ownersFOR each ROW
DECLARE
BEGININSERT INTO t_owners_log VALUES(SYSDATE,: old.id,: old.name,: new.name);
END;