PL/SQL(Procedure Language/SQL)是 Oracle 对 sql 语言的过程化扩展,使 SQL 语言具有过程处理能力。
基本语法结构
[declare
-- 声明变量
]
begin
-- 代码逻辑
[exception
-- 异常处理
]
end;
-- 声明变量
变量名 类型(长度);
-- 变量赋值
变量名:=变量值;
声明变量水费单价、水费字数、吨数、金额。
对水费单价、字数、进行赋值 。吨数根据水费字数换算,规则为水费字数除以
1000,并且四舍五入,保留两位小数。计算金额,金额 = 单价 * 吨数。
输出:单价、数量和金额。
declare
v_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;
-- select into 语法
select 列名 into 变量名 from 表名 where 条件
select into
结果必须是一条记录 ,有多条记录和没有记录都会报错
declare
v_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;
引用某表某列的字段类型
对于不知道表内类型的数据,可以直接获取表内列的类型
declare
v_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); -- 水费金额
标识某个表的行记录类型
一个记录型代表一行数据,类似java编程的实体类
declare
v_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;
发生异常后,语句将停止执行,控制权转移到 PL/SQL 块的异常处理部分
NO_DATA_FOUND
:使用 select into 未返回行TOO_MANY_ROWS
:执行 select into 时,结果集超过一行RAISE
语句显式引发-- 异常语法结构
exception
when 异常类型 then
异常处理逻辑
declare
v_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_ACCOUNT
where 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);
-- 异常处理
exception
when NO_DATA_FOUND then
dbms_output.put_line('未找到数据');
when TOO_MANY_ROWS then
dbms_output.put_line('查询条件有误,返回多条信息');
end;
-- 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 元/吨
根据使用水费的量来计算阶梯水费。
declare
v_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 then
v_money:=v_price1*v_usenum2;
-- 第二个阶梯
elsif v_usenum2>5 and v_usenum2<=10 then
v_money:=v_price1*5+v_price2*(v_usenum2-5);
-- 第三个阶梯
else
v_money:=v_price1*5+v_price2*5+v_price3*(v_usenum2-10);
end if;
-- 信息输出
DBMS_OUTPUT.put_line('金额:'||v_money);
-- 异常处理
exception
when NO_DATA_FOUND then
DBMS_OUTPUT.put_line('没有找到数据');
when TOO_MANY_ROWS then
DBMS_OUTPUT.put_line('返回的数据有多行');
end;
loop
代码
exit when 退出条件;
end loop;
-- 输出1至100
declare
v_num number:=1;
begin
loop
dbms_output.put_line(v_num);
v_num:=v_num+1;
exit when v_num>100;
end loop;
end;
while 条件
loop
代码
end loop;
-- 输出1至100
declare
v_num number:=1;
begin
while v_num<=100
loop
dbms_output.put_line(v_num);
v_num:=v_num+1;
end loop;
end;
for 变量 in 起始值 .. 终止值
loop
代码
end loop;
-- 输出1至100
begin
for v_num in 1 .. 100
loop
dbms_output.put_line(v_num);
end loop;
end;
存放 SQL 语句执行的结果集
-- 声明游标
cursor 游标名称 is SQL语句;
-- 使用游标
open 游标名称
loop
fetch 游标名称 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 ;
条件值有可能是在运行时才能决定的
类似于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 ;
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 ;
存储函数又称为自定义函数。可以接收一个或多个参数,返回一个结果。
create [ or replace ] function 函数名称
(参数名称 参数类型, 参数名称 参数类型, ...)
return 结果变量数据类型
is
变量声明部分;
begin
逻辑部分;
return 结果变量;
[exception
异常处理部分]
end;
创建存储函数,根据地址 ID 查询地址名称
create function fn_getaddress(v_id number)
return varchar2
is
v_name varchar2(30);
begin
select 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
存储过程没有return
,但是可以通过传出函数,传出多个返回值
应用程序可以调用存储过程,执行相应的逻辑,对业务逻辑的封装。
与MVC框架的思想冲突
效率比MVC框架高
create [ or replace ] procedure 存储过程名称
(参数名 类型, 参数名 类型, 参数名 类型)
is|as
变量声明部分;
begin
逻辑部分;
[exception
异常处理部分;]
end;
参数只指定类型,不指定长度
过程参数的三种模式:
IN
:传入参数(默认)OUT
:传出参数 ,主要用于返回程序运行结果IN OUT
:传入传出参数-- 创建
create or replace procedure pro_owners_add
(
v_name varchar2,
v_addressid number,
v_housenumber varchar2,
v_watermeter varchar2,
v_type number
)
is
begin
insert 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
begin
pro_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);
}
}
-- 创建
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
begin
select 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;
-- 调用
declare
v_id number;
begin
pro_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;
}
触发器是一个与表相关联的、存储的PL/SQL程序。
每当一个特定的操作,Oracle自动地执行触发器中定义的语句序列。
触发器可用于
触发器分类
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) |
当用户输入本月累计表数后,自动计算出本月使用数
CREATE OR REPLACE TRIGGER tri_account_update_num1
before
UPDATE OF num1
ON t_account
FOR each ROW
DECLARE
BEGIN
: new.usenum :=: new.num1 -: new.num0;
END;
当用户修改了业主信息表的数据时记录修改前与修改后的值
CREATE TRIGGER tri_owners_log
after
UPDATE OF name
ON t_owners
FOR each ROW
DECLARE
BEGIN
INSERT INTO t_owners_log VALUES(SYSDATE,: old.id,: old.name,: new.name);
END;