PL/SQL
PL/SQL(Procedure Language/SQL)是 Oracle 对 sql 语言的过程化扩展,指 在 SQL 命令语言中增加了过程处理语句(如分支、循环等),使 SQL 语言具有 过程处理能力。把 SQL 语言的数据操纵能力与过程语言的数据处理能力结合起 来,使得 PLSQL 面向过程但比过程语言简单、高效、灵活和实用
基本语法结构
变量
声明变量的语法: 变量名 类型(长度);
变量赋值的语法:
变量名:=变量值
变量的声明
需求: 声明变量水费单价、水费字数、吨数、金额。 对水费单价、字数、进行赋值 。吨数根据水费字数换算,规则为水费字数除以 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_price||'吨
数:'||v_usenum2||'金额:'||v_money);
end;
输出
Select into 方式 赋值
select 列名 into 变量名 from 表名 where 条件
注意:结果必须是一条记录 ,有多条记录和没有记录都会报错
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 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_price||'吨数:'
||v_usenum2||'金额:'||v_money||'上月字数:'||v_num0||'本月
字数'||v_num1);
end;
属性类型
%TYPE 引用型
作用:引用某表某列的字段类型
--属性类型 (引用型 表名.列名%type)
declare
v_price number(10,2);--单价
v_usenum t_account.usenum%type;--水费字数
v_usenum2 number(10,2);--吨数
v_money number(10,2);--金额
v_num0 t_account.num0%type;--上月水表数
v_num1 t_account.num1%type;--本月水表数
begin
v_price:=2.45;--单价赋值
--从数据库中提取
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_usenum||'金额:'||v_money);
end;
%ROWTYPE 记录型 ,上例中的例子可以用下面的代码代替
作用:标识某个表的行记录
--属性类型 (记录型 表名%rowtype )
declare
v_price number(10,2);--单价
v_usenum2 number(10,2);--吨数
v_money number(10,2);--金额
v_account t_account%rowtype;--台账行记录类型
begin
v_price:=2.45;--单价赋值
--从数据库中提取
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_account.usenum||'金额:'||v_money);
end;
异常
在运行程序时出现的错误叫做异常
发生异常后,语句将停止执行,控制权转移到 PL/SQL 块的异常处理部分 异常有两种类型:
- 预定义异常 - 当 PL/SQL 程序违反 Oracle 规则或超越系统限制时隐式 引发
- 用户定义异常 - 用户可以在 PL/SQL 块的声明部分定义异常,自定义的 异常通过
RAISE语句显式引发
预定义异常
Oracle 预定义异常 21 个
| 命名的系统异常 | 产生原因 |
|---|---|
| ACCESS_INTO_NULL | 未定义对象 |
| CASE_NOT_FOUND | CASE 中若未包含相应的 WHEN ,并且没有设置 ELSE 时 |
| COLLECTION_IS_NULL | 集合元素未初始化 |
| CURSER_ALREADY_OPEN | 游标已经打开 |
| DUP_VAL_ON_INDEX | 唯一索引对应的列上有重复的值 |
| INVALID_CURSOR | 在不合法的游标上进行操作 |
| INVALID_NUMBER | 内嵌的 SQL 语句不能将字符转换为数字 |
| NO_DATA_FOUND | 使用 select into 未返回行 |
| TOO_MANY_ROWS | 执行 select into 时,结果集超过一行 |
| ZERO_DIVIDE | 除数为 0 |
| SUBSCRIPT_BEYOND_COUNT | 元素下标超过嵌套表或 VARRAY 的最大值 |
| SUBSCRIPT_OUTSIDE_LIMIT | 使用嵌套表或 VARRAY 时,将下标指定为负数 |
| VALUE_ERROR | 赋值时,变量长度不足以容纳实际数据 |
| LOGIN_DENIED | PL/SQL 应用程序连接到 oracle 数据库时,提供了不正确的用户名或密码 |
| NOT_LOGGED_ON | PL/SQL 应用程序在没有连接 oralce 数据库的情况下访问数据 |
| PROGRAM_ERROR | PL/SQL 内部问题,可能需要重装数据字典& pl./SQL 系统包 |
| ROWTYPE_MISMATCH | 宿主游标变量与 PL/SQL 游标变量的返回类型不兼容 |
| SELF_IS_NULL | 使用对象类型时,在 null 对象上调用对象方法 |
| STORAGE_ERROR | 运行 PL/SQL 时,超出内存空间 |
| SYS_INVALID_ID | 无效的 ROWID 字符串 |
| TIMEOUT_ON_RESOURCE | Oracle 在等待资源时超时 |
语法结构:
--异常处理1
declare
v_price number(10,2);--单价
v_usenum2 number(10,2);--吨数
v_money number(10,2);--金额
v_account t_account%rowtype;--台账行记录类型
begin
v_price:=2.45;--单价赋值
--从数据库中提取
select * into v_account from t_account
where year='2012' and month='01' and owneruuid=200;
v_usenum2:=round( v_account.usenum/1000,2);--吨数
v_money:=v_price*v_usenum2;--金额
DBMS_OUTPUT.put_line('字数:'||v_account.usenum||'金额:'||v_money);
exception
when no_data_found then
DBMS_OUTPUT.put_line('没有找到账务数据');
end;
--异常处理2
declare
v_price number(10,2);--单价
v_usenum2 number(10,2);--吨数
v_money number(10,2);--金额
v_account t_account%rowtype;--台账行记录类型
begin
v_price:=2.45;--单价赋值
--从数据库中提取
select * into v_account from t_account
where year='2012' and month='01' ;
v_usenum2:=round( v_account.usenum/1000,2);--吨数
v_money:=v_price*v_usenum2;--金额
DBMS_OUTPUT.put_line('字数:'||v_account.usenum||'金额:'||v_money);
exception
when no_data_found then
DBMS_OUTPUT.put_line('没有找到账务数据');
when too_many_rows then
DBMS_OUTPUT.put_line('返回多行账务数据');
end;
条件判断
基本语法 1
基本语法2
基本语法3
--条件判断
declare
v_price1 number(10,2);--单价
v_price2 number(10,2);--单价
v_price3 number(10,2);--单价
v_usenum2 number(10,2);--吨数
v_money number(10,2);--金额
v_account t_account%rowtype;--台账行记录类型
begin
v_price1:=2.45;--单价赋值(5吨以下)
v_price2:=3.45;--单价赋值 (5-10吨)
v_price3:=4.45;--单价赋值 (超过10吨)
--从数据库中提取
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;--金额
--阶梯水费计算
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_account.usenum||'金额:'||v_money);
exception
when no_data_found then
DBMS_OUTPUT.put_line('没有找到账务数据');
when too_many_rows then
DBMS_OUTPUT.put_line('返回多行账务数据');
end;
循环
无条件循环
语法结构
--无条件循环:1 到100
declare
v_num number;
begin
v_num:=1;
loop
dbms_output.put_line(v_num);
v_num:=v_num+1;
exit when v_num>100;
end loop;
end ;
条件循环
--有条件循环:1 到100
declare
v_num number;
begin
v_num:=1;
while v_num<=100
loop
dbms_output.put_line(v_num);
v_num:=v_num+1;
end loop;
end;
for循环
-- 输出1到100的数
begin
for v_num in 1 .. 100
loop
dbms_output.put_line(v_num);
end loop;
end;
游标
什么是游标
游标是系统为用户开设的一个数据缓冲区,存放SQL语句的执行结果。我们可以把游标理解为PL/SQL中的结果集
语法结构及示例
在声明区声明游标,语法如下:
cursor 游标名称 is SQL 语句;
使用游标语法
需求:打印业主类型为 1 的价格表
-- 游标 输出结果集
declare
cursor cur_pricetable is select * from t_pricetable where ownertypeid=1;--声明游标
v_pricetable t_pricetable%rowtype;
begin
open cur_pricetable;--打开游标
loop
fetch cur_pricetable into v_pricetable;--提取游标
exit when cur_pricetable%notfound;--如果游标到底端,循环结束
DBMS_OUTPUT.put_line('价格:'||v_pricetable.price||'吨数:'
||v_pricetable.minnum||'-'||v_pricetable.maxnum);
end loop;
close cur_pricetable;--关闭游标
end;
输出结果如下:
带参数的游标
我们的查询语句的条件值有可能是在运行时才能决定的,比如性业主类型, 可能是运行时才可以决定,那如何实现呢?我们接下来学习带参数的游标,修改 上述案例
--带参数的游标
declare
cursor cur_pricetable(v_type number)
is select * from t_pricetable where ownertypeid=v_type;--声明游标
v_pricetable t_pricetable%rowtype;
begin
open cur_pricetable(1);--打开游标时指定参数值
loop
fetch cur_pricetable into v_pricetable;--提取游标
exit when cur_pricetable%notfound;--如果游标到底端,循环结束
DBMS_OUTPUT.put_line('价格:'||v_pricetable.price||'吨数:'
||v_pricetable.minnum||'-'||v_pricetable.maxnum);
end loop;
close cur_pricetable;--关闭游标
end;
输出结果
for 循环提取游标值
我们每次提取游标,需要打开游标 关闭游标 循环游标 提取游标 控制循环的 退出等等,好麻烦!有没有更简单的写法呢?有!用 for 循环一切都那么简单, 上例的代码可以改造为下列形式
--for循环 带参数的游标
declare
cursor cur_pricetable(v_type number)
is select * from t_pricetable where ownertypeid=v_type;--声明游标
--v_pricetable t_pricetable%rowtype;
begin
for v_pricetable in cur_pricetable(1)
loop
DBMS_OUTPUT.put_line('价格:'||v_pricetable.price||'吨数:'
||v_pricetable.minnum||'-'||v_pricetable.maxnum);
end loop;
end;
输出结果如下: