【Oracle】Oracle编程PLSQL

Oracle编程

一、PL/SQL

1、PL/SQL概述

PL/SQL(Procedure Language/SQL)是 Oracle 对 sql 语言的过程化扩展,使 SQL 语言具有过程处理能力。

基本语法结构

[declare 
	-- 声明变量
]

begin
	-- 代码逻辑 

[exception
	-- 异常处理
]

end;
2、变量
1)变量的声明与赋值
-- 声明变量
变量名 类型(长度);

-- 变量赋值
变量名:=变量值;
2)直接赋值

声明变量水费单价、水费字数、吨数、金额。

对水费单价、字数、进行赋值 。吨数根据水费字数换算,规则为水费字数除以

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;
2)select into 赋值
-- 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;
3、属性类型
1)%TYPE 引用型

引用某表某的字段类型

对于不知道表内类型的数据,可以直接获取表内列的类型

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);				-- 水费金额
2)%ROWTYPE 记录型

标识某个表的记录类型

一个记录型代表一行数据,类似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;
4、异常(例外)

发生异常后,语句将停止执行,控制权转移到 PL/SQL 块的异常处理部分

  • 预定义异常:Oracle预先定义的异常
    • 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;
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 元/吨

根据使用水费的量来计算阶梯水费。

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;
6、循环
1)loop无条件循环
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;
2)while条件循环
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;
3)for循环
for 变量 in 起始值 .. 终止值
loop
	代码
end loop;
-- 输出1至100
begin
	for v_num in 1 .. 100
	loop
		dbms_output.put_line(v_num); 
	end loop;
end;
7、游标
1)游标概述

存放 SQL 语句执行的结果集

【Oracle】Oracle编程PLSQL_第1张图片

2)游标的语法
-- 声明游标
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 ;
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
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

三、存储过程

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

四、触发器

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_num1
    before 
    UPDATE OF num1 
    ON t_account 
    FOR each ROW
DECLARE
BEGIN
	: new.usenum :=: new.num1 -: new.num0;
END;
2)后置触发器

当用户修改了业主信息表的数据时记录修改前与修改后的值

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;

你可能感兴趣的:(oracle,数据库)