PL/SQL(Procedural Language/SQL)编程简介 游标,过程,函数,包,触发器

块结构

PL/SQL程序可划分成称为块的结构,每一个块都包含有PL/SQL和SQL语句。

典型的PL/SQL块包含如下结构:

[DECLARE

declaration_statements 声明PL/SQL块其余部分使用的变量。DECLARE是可选的。

]

BEGIN

executable_statements  块中实际可执行的语句,其中可能包括诸如循环、条件逻辑等任务的语句

[EXCEPTION

exception_handing_statements 负责处理当块运行时可能发生的任何可执行错误。EXCEPTION是可选的。

]

END;

/

例子:根据给出的矩形面积和高度计算其宽度

SET SERVEROUTPUT ON 打开服务器输出以便在SQL*Plus中运行脚本时可以在屏幕上看到由DBMS_OUTPUT.PUT_LINE()产生的行。

DECLARE

v_width INTEGER;

v_height INTEGER;

v_area INTEGER;

BEGIN

--set the width equal to the area divided by the height  注释

v_width := v_area / v_height;

DBMS_OUTPUT.PUT_LINE('v_width = ' || v_width); DBMS_OUTPUT是Oracle数据库内置的代码包,DBMS_OUTPUT包含允许将值输出到屏幕上的过程。

EXCEPTION

WHEN ZERO_DIVIDE THEN

DBMS_OUTPUT.PUT_LINE('Division by zero');

END;

/  PL/SQL块结束


变量和类型

可通过%TYPE关键字来定义变量的类型,使用与表中指定列相同的类型。

v_product_price products.price%TYPE  这个变量的类型与products表的price列类型相同。


条件逻辑

IF condition1 THEN

statements1

ELSEIF condition2 THEN

statements2

ELSE

statements3

END IF;


循环

简单循环 直到显式结束循环之前一直运行。

LOOP

statements

END LOOP;  要结束简单循环,可以使用EXIT立即结束循环,EXIT WHEN 在指定条件出现时结束循环。

例:

v_counter := 0;

LOOP 

v_counter := v_counter + 1;

EXIT WHEN v_counter = 5;

END LOOP;   EXIT WHEN语句可以出现在循环代码中的任何位置。

可以使用CONTINUE或CONTINUE WHEN语句结束循环的当前迭代。CONTINUE无条件结束循环当前迭代,并继续执行下一个迭代。CONTINUE WHEN在满足特定条件时结束循环的当前迭代,然后继续执行下一个迭代。

例:

v_counter := 0;

LOOP

-- after the CONTINUE statement is executed, control returns here

v_counter := v_counter + 1;

IF  v_counter = 3 THEN

CONTINUE; -- end current iteration unconditionally

END IF;

EXIT WHEN v_counter = 5;

END LOOP;   CONTINUE或CONTINUE WHEN 不能跨过过程、函数或方法的边界。

WHILE循环  在某个特定条件出现之前一直运行

WHILE condition LOOP

statements

END LOOP;

FOR 循环  运行预先确定的次数,给循环变量指定下限和上限来确定循环运行的次数,然后,循环变量在每次循环中递增(或递减)。

FOR loop_variable IN [REVERSE]  lower_bound.. upper_bound LOOP

statements

END LOOP;在没有使用REVERSE的情况下,循环变量初始化为下限值。使用REVERSE,循环变量初始化为上限值。

例:

FOR v_counter2 IN 1..5 LOOP

DBMS_OUTPUT.PUT_LINE(v_counter);

END LOOP;


游标 获取查询返回的记录。在通过查询将记录检索到游标中后,可以一次从游标中取出一行。

使用游标步骤一:声明用于保存列值的变量

DECLARE 

v_product_id products.product_id%TYPE;

v_name products.name%TYPE;

v_price products.price%TYPE;

步骤二:声明游标,游标的声明由游标名和希望执行的查询组成。

CURSOR cursor_name IS

select_statement; 查询在打开游标前并不会真正运行。

步骤三:打开游标

OPEN v_product_cursor

步骤四:从游标中取得记录

FETCH cursor_name

INTO variable[, variable ...]

当游标包含多条记录,要通过循环一次读取每一条记录。为了确定循环是否结束,可以使用布尔变量v_product_cursor%NOTFOUND。当从v_product_cursor中不能再读出记录时,这个变量就为真。

步骤五:关闭游标 释放系统资源

CLOSE v_product_cursor;

完整示例:

SET SERVEROUTPUT ON


DECLARE

--step 1: declare the variables

v_product_id products.product_id%TYPE;

v_name products.name%TYPE;

v_price products.price%TYPE;


--step 2:declare the cursor

CURSOR v_product_cursor IS

SELECT product_id, name, price

FROM  products

ORDER BY product_id

BEGIN

--step 3:open the cursor

OPEN v_product_cursor;


LOOP

--step 4 :fetch the rows from the cursor

FETCH v_product_cursor

INTO v_product_id,v_name,v_price;

EXIT WHEN v_product_cursor%NOTFOUND


--use DBMS_OUTPUT.PUT_LINE() to display the variables

DBMS_OUTPUT.PUT_LINE(

'v_product_id = ' || v_product_id || ', v_name = name' || v_name  ||

', v_space = ' v_space

);

END LOOP;


--step 5: close the cursor

CLOSE v_product_cursor;

END

/

游标与FOR循环

利用FOR循环可以访问游标中的记录。当使用FOR循环时,可以不显式的打开和关闭游标。

FOR循环会自动执行这些操作

OPEN-FOR语句

可以将游标分配给不同的查询,因此可以更加灵活的处理游标。

无约束游标

前面介绍的游标都有具体的返回类型,这些游标称为约束游标。约束游标的返回类型必须与游标运行的查询中的列相匹配。无约束游标没有返回类型,因此可以运行任何查询。


异常

ZERO_DIVIDE异常  试图用一个数字除以零的时候发生

DUP_VAL_ON_INDEX试图向具有唯一性索引约束的列中插入重复的值。

INVALID_NUMBER试图将无效的字符串转换成数字时。

ORTHERS异常可以处理所有异常。


过程

过程包含一组SQL和PL/SQL语句。过程可将业务逻辑集中在数据库中,任何能够访问数据库的程序都可以使用过程。

CREATE PROCEDURE [OR REPLACE] procedure_name

[ ( parameter_name [IN | OUT | IN OUT ] type [, ...]) ] 

{ IS | AS}

BEGIN 

procedure_body;

END procedure_name;


例子:

CREATE PROCEDURE update_product_price (

p_product_id IN products.product_id%TYPE,

p_factor IN  NUMBER

) AS

v_product_count INTEGER;

BEGIN

SELECT COUNT(*)

INTO v_product_count

FROM products

WHERE product_id = p_product_id

IF v_product_count = 1THEN

UPDATE products

SET price = price * p_factor

WHERE product_id = p_product_id;

COMMIT;

END IF;

EXCEPTION 

 WHEN OTHERS THEN

ROLLBACK;

END update_product_price

/


调用过程: 

位置表示法CALL update_product_price(1,1.5);

命名表示法CALL update_product_price(p_factor => 1.3,p_product_id => 2);

混合表示法CALL update_product_price(3,p_factor => 1.7);

获取有关过程的信息:user_procedures(all_procedures)视图

删除过程DROP PROCEDURE update_product_price;

查看过程中的错误:SHOW ERRORS

函数

函数与过程很相似,唯一区别在于函数必须向调用它的语句返回一个值。

存储过程和函数有时合起来被称为存储子程序。

创建函数

CREATE FUNCTION [OR REPLACE] function_name

[ (parameter_name [ IN | out | IN OUT] type [, ...] ) ]

RETURN type

{ IS | AS  }

BEGIN

function_body

END function_name

例子1:

CREATE FUNCTION circle_area (

p_radius IN NUMBER

) RETURN NUMBER AS 

v_pi NUMBER  := 3.1415926;

v_area NUMBER;

BEGIN

v_area := v_pi * POWER(p_radius,2);

RETURN v_area;

END circle_area;

/

例子2:

CREATE FUNCTION average_product_price (

p_product_type_id IN INTEGER

) RETURN NUMBER AS

v_average_product_price NUMBER;

BEGIN

SELECT AVG(price)

INTO v_average_product_price

FROM products

WHERE product_type_id = p_product_type_id;

RETURN v_average_product_price;

END average_product_price;

/

调用函数

SELECT circle_area(2) FROM dual;

命名表示法: SELECT circle_area(p_radius => 4) FROM dual;

SELECT average_product_price(1) FROM dual;

获取有关函数的信息:user_procedures(all_procedures)视图。

删除函数:DROP FUNCTION function_name;


包(package)

包可以将彼此相关的功能划分到一个自包含的单元中。通过这种方式将PL/SQL代码模块化,可以构建供其他编程人员重用的代码库。

事实上,ORACLE数据库提供了包库,可用来访问外部文件,管理数据库、生成HTML等等。

包通常由两部分组成:规范(specification)和包体(body)。

包的规范列出可用的过程、函数、类型和对象。所有的数据库用户都可以访问这些条目,因此这些条目称为公有项目(只有具有包访问权限的用户才能使用)。规范中通常不包括构成这些过程和函数的代码,包体中才包含实际的代码。

包体中任何在规范中未列出的项目对于这个包体都是私有对象。私有项目只能用在包体内。通过将公有项目和私有项目结合起来,可以构建出非常复杂的包结构,这种复杂性外部是看不到。这也是所有编程工作的主要目标之一:对用户隐藏复杂性。

创建包规范

CREATE [ OR REPLACE ] PACKAGE package_name

{ IS | AS}

package_specification

END package_name;

例子:

CREATE PACKAGE product_package AS

TYPE t_ref_cursor IS REF  CURSOR;

FUNCTION get_products_ref_cursor  RETURN  t_ref_cursor;

PROCEDURE  update_product_price  (

p_product_id  IN products.product_id%TYPE,

p_factor IN  NUMBER

);

END  product_package;

/

创建包体

CREATE [OR REPLACE] PACKAGE BODY package_name

{IS | AS }

package_body

END package_name;

例子:

CREATE PACKAGE BODY product_package  AS

FUNCTION get_products_ref_cursor

RETURN t_ref_cursor  IS

v_products_ref_cursor  t_ref_cursor;

BEGIN

OPEN  v_products_ref_cursor  FOR

SELECT  products_id,name,price

FROM  products;

RETURN  v_products_ref_cursor;

END  get_products_ref_cursor;


PROCEDURE  update_product_price (

p_product_id  IN  products.product_id%TYPE,

p_factor  IN  NUMBER

) AS

v_product_count  INTEGER;

BEGIN

SELECT  COUNT(*)

INTO  v_product_count

FROM  products

WHERE product_id = p_product_id;


IF v_product_count = 1 THEN 

UPDATE products

SET price = price * p_factor

WHERE product_id = p_product_id;

COMMIT;

END IF;

EXCEPTION

WHEN OTHERS THEN 

ROLLBACK;

END update_product_price;

END product_package;

/

调用包中的函数和过程

SELECT product_package.get_products_ref_cursor  FROM dual;

CALL  product_package.update_product_price(3,1.25);

获取有关包中函数和过程的信息:(user_procedures)

删除包:DROP PACKAGE  product_package;


触发器trigger

触发器是当特定的SQL DML 语句,如INSERT,UPDATE或DELETE语句在特定的数据库表上运行时,由数据库自动激活的过程。

触发器对于实现表中某些列值的高级变更审计等功能非常有用。

触发器运行的时机

trigger可以在DML语句运行之前和之后被激活。 行级触发器(每行触发一次)与语句级触发器(DML语句触发一次)

创建触发器

CREATE [ OR REPLACE ] TRIGGER  trigger_name

{BEFORE  |  AFTER  |  INSTEAD  OF  |  FOR}  trigger_event

ON  table_name

[ FOR  EACH  ROW ]

[{FORWARD | REVERSE } CROSSEDITION ]

[{FOLLOWS | PRECEDES}  schema.other_trigger}

[{ENABLE | DISABLE}]

[WHEN trigger_condition]]

BEGIN

trigger_body

END trigger_name;

例子:

CREATE TRIGGER before_product_price_update

BEFORE UPDATE OF price

ON products

FOR EACH ROW WHEN (new.price < old.price * 0.75)

BEGIN

dbms_output.put_line('pruduct_id = ' || :old.product_id);

dbms_output.put_line('Old price = ' || :old.price);

dbms_output.put_line('New price = ' || :new.price);

dbms_output.put_line('The price reduction is more than 25%');


INSERT INTO product_price_audit (

product_id,old_price,new_price

) VALUES (

:old.product_id, :old.price, :new.price

);

END before_product_price_update;

/

激活触发器

SET SERVEROUTPUT ON 

UPDATE products

SET price = price * .7

WHERE product_id IN (5,10);

获得有关触发器的信息user_trigger(all_trigger)视图。

禁用和启用触发器

ALTER TRIGGER before_product_price_update DISABLE;

ALTER TRIGGER before_product_price_update ENABLE;

删除触发器

DROP TRIGGER before_product_price_update ;


Oracle Darabase 11g新增加的PL/SQL特性。

SIMPLE_INTEGER类型,当不需要存储NULL且对计算中产生的溢出截断不介意时,应该在PL/SQL中使用SIMPLE_INTEGER。否则就应该使用BINARY_INTEGER。


在PL/SQL中使用序列。


PL/SQL本地机器代码生成

默认情况下,每个PL/SQL程序单元都被编译成中间格式,即机器可读的代码。这些代码存储在数据库中,每次代码运行时需要进行解释。使用PL/SQL本地编译,PL/SQL被转换为本地代码,并存储在共享库中。

本地代码的运行速度比中间代码的运行速度快得多,因为本地代码在运行之前不需要解释。










  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值