您的位置:首页 > 数据库

PL/Sql循序渐进全面学习教程(下)(ZT)

2007-01-30 09:52 337 查看
课程一、声明变量
本课重点:
1、了解基本的PLSQL块和区域
2、描述变量在PLSQL中的重要性
3、区别PLSQL与非PLSQL变量
4、声明变量
5、执行PLSQL块
一.概述
1、PLSQL 块结构:
DECLARE --- 可选
变量声明定义
BEGIN ---- 必选
SQL 和PLSQL 语句
EXCEPTION ---- 可选
错误处理
END;---- 必选
二、实例:
declare
vjob varchar(9);
v_count number:=0;
vtotal date:=sysdate+7;
c_tax constant number(3,2):=8.25;
v_valid boolean not null:=true;
begin
select sysdate into vtotal from dual;
end;
/
上例中,如果没有这个SELECT语句,会如何?
出错,说明必须有STATEMENTS
如果: select sysdate from dual into vtotal ;
同样,也不行。而且变量与赋值的类型要匹配。
三、%TYPE的属性
声明一个变量使之与数据库某个列的定义相同或与另一个已经定义过的变量相同
所以%TYPE要作为列名的后缀:如:
v_last_name s_emp.last_name%TYPE;
v_first_name s_emp.first_name%TYPE; --这样做的好处是我们不必去知晓此列的类型与定义
或:v_balance NUMBER(7,2);
v_minimum_balance v_balance%TYPE := 10;
四、声明一个布尔类型的变量
1 只有TRUE、FALSE、NULL可以赋值给BOOLEAN变量
2 此变量可以接逻辑运算符NOT、AND、OR。
3 变量只能产生TRUE、FALSE、NULL。
实例:
VSAL1:=50000;
VSQL2:=60000;
VCOMMSAL BOOLEAN:=(VSAL1<VSQL2);
--其实是把TRUE赋值给此变量。
五、BLOB 类型的变量
共有CLOB、BLOB、BFILE、NCLOB几种,这里不做为重点。
六:使用HOST VARIABLES
SQL> variable n number
SQL> print n
:n = v_sal /12;
:n这个加了:前缀的变量不是PLSQL变量,而是HOST。
七、以下几个PLSQL声明变量,哪个不合法?
A 、DECLARE
V_ID NUMBER(4);
B、DECLARE
V_X,V_Y,V_Z VARCHAR2(9);
C、DECLARE
V_BIRTH DATE NOT NULL;
D、DECLARE
V_IN_STOCK BOOLEAN:=1;
E、DECLARE
TYPE NAME_TAB IS TABLE OF VARCHAR2(20)
INDEX BY BINARY_INTEGER;
DEPT_NAME NAME_TAB;
课程二、写执行语句
本课重点:
1、了解PLSQL执行区间的重要性
2、写执行语句
3、描述嵌套块的规则
4、执行且测试PLSQL块
5、使用代码惯例
一、PLSQL 块的语法规则:
1、语句可以跨跃几行。
2、词汇单元可以包括:分隔符、标识符、文字、和注释内容。
3、分隔符:
+-*/=<>||....
4、标识符:
最多30个字符,不能有保留字除非用双引号引起。
字母开头,不与列同名。
5、文字串:如 V_ENAME:='FANCY';要用单引号括起来。
数值型可以用简单记数和科学记数法。
6、注释内容:单行时用-- 多行用/* */
与C很相似
二、SQL函数在PL/SQL的使用:
1、可用的:
单行数值型、字符型和转换型,日期型。
2、不可用的:
最大、最小、DECODE、分组函数。
实例:
BEGIN
SELECT TO_CHAR(HIREDATE,'MON,DD,YYYY') FROM EMP;
END;
V_comment:=user||':'||sysdate; -- 会编译出错
V_comment:=user||':'||to_char(sysdate); --正确
如果有可能,PLSQL都会进行数据一致性的转换,但ORACLE推荐你应该进行显示的转换,因为这样会提高性能。
三、嵌套块和变量作用区域
1、执行语句允许嵌套时嵌套。
2、嵌套块可以看作正常的语句块。
3、错误处理模块可以包括一个嵌套块
4、exponential指数 逻辑、算数、连接、小括号
5、看正面实例:
declare
vjob varchar(9);
v_count number:=0;
vtotal date:=sysdate +7;
c_tax constant number(3,2):=8.25;
v_valid boolean not null:=true;
ttt vtotal%type;
begin
--select sysdate into vtotal from dual;--体会有无此句与结果的影响
dbms_output.put_line (vtotal);
end;
/
注意:在执行块之前,要在SQL PLUS中执行:SET SERVEROUTPUT ON
四、以实例来说明函数的参数声明作用域
declare
v_weight number(3):=600;
v_message varchar2(255):='product10000';
begin
declare
--sub-block
v_weight number(3):=1;
v_message varchar2(255):='pro300';
begin
v_weight:=v_weight +1;
end;
v_weight:=v_weight +1;
v_message:=v_message || 'my name';
end;
/
子块中的V_WEIGHT值为 2
我们可以在子块中加入:dbms_output.put_line('subblock value is '||v_weight);
在主体中加入:dbms_output.put_line('main value is '||v_weight);
我们发现MAINBLOCK中V_WEIGHT为 601
改动:
1、在主块的声明中加 v_date date default sysdate;
在子块中加入:dbms_output.put_line('subblock date value is '||v_date);
执行结果:subblock date value is 22-11月-01
****说明:主块中的变量,如果子块中没有同名变量声明,则继承主块中的声明和初始化值;
2、在子块中加入:v_sub char(9);
dbms_output.put_line('subblock char value is '||v_sub);
此时正常输出。
在主块中加入:dbms_output.put_line('main char value is '||v_sub);
输出:ORA-06550: 第 21 行, 第 45 列:
PLS-00201: 必须说明标识符 'V_SUB'
说明:
子块中声明的变量主块中并不知晓,因此出错。
了解了此实例,一切情况的变量的值的走向就都明了了。
课程三、与ORACLE SERVER交互
一、PLSQL中的SQL语句:
SELECT、DML、COMMIT、ROLLBACK、SAVEPOINT、CURSOR
特殊强调:PLSQL不支持DCL,不要问为什么。(DBMS_SQL package allows you to issue DDL and DCL statements.)
二、SELECT
SELECT select_list
INTO variable_name | record_name
FROM table
WHERE condition;
例:
SQL> r
1 declare
2 v_deptno number(2);
3 v_loc varchar2(15);
4 begin
5 select deptno,loc
6 into v_deptno,v_loc
7 from dept
8 where dname='SALES';
9 DBMS_OUTPUT.PUT_LINE (V_deptno ||' and '||v_loc);
10 end;
选取字段与变量个数和类型要一致。声明的变量一定要在SIZE上大于返回的赋值,否则提示缓冲区溢出。
如果SELECT语句没有返回值:ORA-01403: 未找到数据
ORA-06512: 在line 5
如果有多个值返回:ORA-01422: 实际返回的行数超出请求的行数
这些我们到了错误处理时会逐一讲解。
上面的例子可以改为:
declare
v_deptno dept.deptno%type;
v_loc dept.loc%type;
begin
select deptno,loc
into v_deptno,v_loc
from dept
where dname='SALES';
DBMS_OUTPUT.PUT_LINE (V_deptno ||' and '||v_loc);
end;
/
这样,可以在未知其他字段大小和类型的时候定义变量,提高兼容性。
三、DML 操作:
1、实例:
declare
v_empno emp.empno%type;
begin
select max(empno)
into v_empno
from emp;
v_empno:=v_empno+1;
insert into emp(empno,ename,job,deptno)
values(v_empno,'asdfasdf','ddddd',10);
end;
/
这样也可以实现如SEQUENCE一样的编号唯一递增。
2、更新和删除:
这个较为简单:
DECLARE
V_DEPTNO EMP.DEPtno%type :=10;
begin
delete from emp
where deptno=v_deptno;
end;
/
PLSQL首先检查一个标识符是否是一个数据库的列名,如果不是,再假定它是一个PLSQL的标识符。所以如果一个PLSQL的变量名为ID,列中也有个ID,如:
SELECT date_ordered, date_shipped
INTO date_ordered, date_shipped
FROM s_ord
WHERE id = id;
就会返回TOO MANY ROWS,这是要尽量避免的。
四、SQL CURSOR
游标是一个独立SQL工作区,有两种性质的游标:
隐式游标: 当PARSE 和EXECUTE 时使用隐式游标。
显式游标: 是由程序员显式声明的。
游标的属性:
SQL%ROWCOUNT:一个整数值,最近SQL语句影响的行数。
SQL%FOUND BOOLEAN属性,如果为TRUE,说明最近的SQL STATEMENT有返回值。
SQL%NOTFOUND 与SQL%FOUND相反
SQL%ISOPEN 在隐式游标中经常是FALSE,因为执行后立即自动关闭了。
SQL> variable row_de number
SQL> r
1 declare
2 v_deptno number:=10;
3 begin
4 delete from emp where
5 deptno=v_deptno;
6 :row_de:=sql%rowcount;
7 end;
PL/SQL 过程已成功完成。
SQL> print row_de --这是一个SQL PLUS变量
ROW_DE
----------
4
这时其实并没有真正的删除,而是需要 COMMIT或ROLLBACK,来完成事务。
课程四、 编写控制结构语句
一、控制执行流
例子:IF V_ENAME='OSBORNE' THEN
V_MGR:=22;
END IF;
这里我们可以注意,PLSQL和C语言或JAVA在条件上的不同,=代表关系运算,而:=代表赋值。
看一个函数:
create FUNCTION calc_val
(v_start IN NUMBER)
RETURN NUMBER
IS
BEGIN
IF v_start > 100 THEN
RETURN (2 * v_start);
ELSIF v_start >= 50 THEN
RETURN (.5 * v_start);
ELSE
RETURN (.1 * v_start);
END IF;
END calc_val;
二、注意LOGIC TABLE中的逻辑对应关系
1、NOT、AND、OR
2、任何表达式中含有空值结果都为 NULL
3、连接字符串中含有空值会把NULL作为 EMPTY STRING
declare
v_deptno dept.deptno%type;
v_loc dept.loc%type;
V_FLAG BOOLEAN ;
V_REC BOOLEAN :=FALSE; --此值改为TRUE、NULL、FALSE进行不同的比较
V_AVA BOOLEAN:=NULL;
begin
V_FLAG:=V_REC AND V_AVA;
IF V_FLAG=TRUE THEN
DBMS_OUTPUT.PUT_LINE ('TRUE');
ELSIF V_FLAG=FALSE THEN
DBMS_OUTPUT.PUT_LINE ('FALSE');
ELSE
DBMS_OUTPUT.PUT_LINE ('NULL');
END IF;
end;
/
值得注意的是:NULL AND FALSE ---> FALSE
三、基本循环基础:
1、LOOP
statement1;
statement2;
. . .
EXIT [WHEN condition];
END LOOP;
例如:
v_ord_id s_item.ord_id%TYPE := 101;
v_counter NUMBER (2) := 1;
BEGIN
. . .
LOOP
INSERT INTO s_item (ord_id, item_id)
VALUES (v_ord_id, v_counter);
v_counter := v_counter + 1;
EXIT WHEN v_counter > 10;
END LOOP;
2、FOR循环:
FOR index IN [REVERSE] lower_bound..upper_bound LOOP
statement1;
statement2;
. . .
END LOOP;
实例:DECLARE
V_LOWER NUMBER:=1;
V_UPPER NUMBER:=23;
BEGIN
DBMS_OUTPUT.PUT_LINE('');
FOR I IN V_LOWER..V_UPPER LOOP
DBMS_OUTPUT.PUT_LINE(I);
END LOOP;
END;
3、WHILE 循环:
WHILE condition LOOP
statement1;
statement2;
. . .
END LOOP;
4、循环是可以多层嵌套的。可以用<<LABEL>>做循环的标签。
BEGIN
<<Outer–loop>>LOOP
v_counter :=v_counter+1;
EXIT WHEN v_counter>10;
<<Inner–loop>>LOOP
...
EXIT Outer_loop WHEN total_done = ’YES’;
–– Leave both loops
EXIT WHEN inner_done = ’YES’;
–– Leave inner loop only
...
END LOOP Inner_Loop;
...
END LOOP Outer_loop;
END;
课程五、使用组合数据类型* 游标操纵数据
本课重点:
1、创建用户自定义的PLSQL记录
2、利用%ROWTYPE属性来创建记录
3、创建PLSQL表
4、描述记录、表、记录的表之间的区别
一、合成数据类型
1、类型分为PLSQL记录和PLSQL表
2、包含内部组件
3、可重用
二、PLSQL记录
与3GL中的记录结构相似
与数据库表是两回事
是一个方便的途径FETCH一些行FROM一个表来进行相关处理。
declare
vjob varchar(9);
v_count number:=0;
vtotal date:=sysdate +7;
c_tax constant number(3,2):=8.25;
v_valid boolean not null:=true;
ttt vtotal%type;
type emp_record_type is record
(empno number not null:=100,
ename emp.ename%type,
job emp.job%type);
emp_record emp_record_type;
begin
--select sysdate into vtotal from dual;--体会有无此句与结果的影响
dbms_output.put_line (vtotal);
end;
主要看TYPE RECORD出现的位置。每一个例子都是可以成功执行的。
我们也可以利用原有的表结构:
DECLARE
EMP_RECORD EMP%ROWTYPE;
三、游标操纵数据
PLSQL游标提供了一种从数据库提取多行数据,然后对每行数据进行单独处理的方法。
1.两种游标:显式游标、隐式游标
2.显式游标:操纵步骤如下:声明游标、打开游标、从游标中取回数据、关闭游标
声明游标:
DECLARE CURSOR_NAME
IS
SELECT STATMENT
能够控制游标的,唯一参数是INIT.ORA中的OPEN_CURSORS,我原来以为是客户端最多可以打开多少个游标,但有本书上讲这是用于管理游标的内存的数量。
DECLARE
CURSOR C_NAME
IS
SELECT ENAME FROM EMP
WHERE DEPTNO IN (SELECT DEPTNO FROM DEPT
WHERE CITY_ID=‘BJ’)
--- 说明游标可以用子查询
打开游标:
OPEN CURSOR_NAME;
这时游标将它的指针指向活动集的开始,指针指向第一条记录的前面是因为它还没有执行FETCH命令。如果试图打开一个已经打开的游标,将出错:
ORA-06511:PL/SQL:CURSOR ALREADY OPEN
我们可以这样:
IF NOT C_NAME%ISOPEN
THEN
OPEN C_NAME;
END IF;
从游标中取回数据:
FETCH CURSOR_NAME INTO RECOR-LIST;
关闭游标:CLOSE CURSOR_NAME
DECLARE
myname varchar2(22);
CURSOR C_NAME
IS
SELECT ENAME FROM EMP;
begin
IF NOT C_NAME%ISOPEN
THEN
OPEN C_NAME;
end if;
LOOP
FETCH c_name into myname;
dbms_output.put_line (myname);
exit when c_name%notfound;
end loop;
close c_name;
end;
/--我们将对以上程序进行变形,形成复杂的光标利用。
DECLARE
myname varchar2(22);
thisdeptno scott.emp.deptno%type;
CURSOR C_NAME
IS
SELECT ENAME,deptno FROM EMP order by deptno desc;
begin
IF NOT C_NAME%ISOPEN
THEN
OPEN C_NAME;
end if;
LOOP
FETCH c_name into myname,thisdeptno;
dbms_output.put_line (myname||','||thisdeptno || ',' || to_char(c_name%rowcount));
exit when c_name%notfound;
end loop;
dbms_output.put_line ('the Total record is fetched is ' || to_char(c_name%rowcount));
close c_name;
end;
/
我们增加变量,进行用了排序,使用了光标属性,大家看结果发生的变化,想想为什么。
实例精华!!!: DECLARE
myname varchar2(22);
ii number;
thisdeptno scott.emp.deptno%type;
CURSOR C_NAME
IS
SELECT * FROM EMP order by deptno desc;
emp_record c_name%rowtype;
begin
ii:=1;
for emp_record in c_name loop
dbms_output.put_line(ii);
ii:=ii+1;
end loop;
end;
/
--这里使用了游标FOR循环,在FOR循环的开始,进行、和END LOOP,分别隐式进行了游标的打开、FETCH和CLOSE。
我们甚至可以不声明游标:FOR emp_record in (SELECT * FROM DEPT) loop
这种技术被称为显式游标的自动化。
在上面,我们可以将一个表的所有字段输出,如我们将PUT_LINE的II改为emp_record.ename,就可以输出一个字段内容。
这种方式非常简单而且效率较高。
为了测试光标属性的重要性,我们做一个以下的过程:
create or replace PROCEDURE change_salary
(v_emp_id IN NUMBER, -- formal parameters
v_new_salary IN NUMBER)
IS
BEGIN -- begin PL/SQL block
UPDATE emp
SET sal = v_new_salary
WHERE empno = v_emp_id;
COMMIT;
END change_salary;
/
这样,我们在匿名块中,
UPDATE DEPT
SET DNAME='MY DEPT' WHERE ....;
IF SQL%FOUND THEN
COMMIT;
ELSE
change_salary(7369,9000);
END IF;
我们看到我们通过流程控制了不同的执行结果,对于过程,我们可以用以下几种方法调用:
在SQLPLUS中:CALL change_salary(7369,9000);
EXECUTE change_salary(7369,9000);
在一个块中,如:
begin
change_salary(7369,9000);
end;
/
第六课、异常处理
1、定义PLSQL异常
2、列举不同的异常处理方法
3、捕获非预期的错误
4、描述异常的影响
5、定制异常的返回信息 一、PLSQL异常处理
异常是由ORACLE错误或显式的抛出一个错误产生的。
如何处理:
用一个处理程序来捕获它;
将它传递给CALLING ENVIRONMENT
二、异常的类型:
ORACLE SERVER 预定义错误
非ORACLE SERVER 预定义错误,但也是ORACLE SERVER 的标准错误
用户自定义异常
三、捕捉异常的要点:
Place the WHEN OTHERS clause after all other exception handling clauses.
You can have at most one WHEN OTHERS clause.
Begin exception-handling section of the block with the keyword EXCEPTION.
Define several exception handlers, each with their own set of actions, for the block.
When an exception occurs, PL/SQL will process only one handler before leaving the block.

EXCEPTION
WHEN exception1 [OR exception2 . . .] THEN
statement1;
四、常用错误:
NO_DATA_FOUND ORA-01403
TOO_MANY_ROWS ORA-01422
INVALID_CURSOR ORA-01001
ZERO_DIVIDE ORA-01476
DUP_VAL_ON_INDEX ORA-00001
五、实例
PROCEDURE elim_inventory
(v_product_id IN s_product.id%TYPE) IS
v_id s_product.id%TYPE;
BEGIN
SELECT id
INTO v_id
FROM s_product
WHERE id = v_product_id;
DELETE FROM s_inventory
WHERE product_id = v_product_id;
COMMIT;
EXCEPTION
WHEN NO_DATA_FOUND THEN
ROLLBACK;
TEXT_IO.PUT_LINE(TO_CHAR(v_product_id)||
’ is invalid.’);
WHEN TOO_MANY_ROWS THEN
ROLLBACK;
TEXT_IO.PUT_LINE(’Data corruption in S_PRODUCT.’);
WHEN OTHERS THEN
ROLLBACK;
TEXT_IO.PUT_LINE(’Other error occurred.’);
END elim_inventory;
六、使用non-predefined Oracle7 Server error
DECLARE
E_PRO EXCEPTION;
PRAGMA EXCEPTION_INIT(E_PRO,ERROR_NUMBER);
BEGIN
......
EXCEPTION
WHEN E_PRO THEN
DBMS_OUTPUT.PUT_LINE('ASDLKFJKASDJFASJDFLKASDF');
......
END;
七、用户自定义
exception EXCEPTION;
RAISE exception; EXCEPTION
WHEN E_PRO THEN
DBMS_OUTPUT.PUT_LINE('ASDLKFJKASDJFASJDFLKASDF');
......
END; 这里,只有用户自定义异常是要显式声明的,其他两个不用。
在SUN OS5.8中,进行SVRMGRL> OERR ORA 01840 可返回信息
或查错误代码:
HTTP://TECHNET.ORACLE.COM/DOC/SERVER.815/A67785/E1500.HTM
题外话,create public database link otlink connect to system identified
by manager using 'oratest';
说回来,以下两个函数:
SQLCODE ----Returns the numeric value for the error code. You can assign it
to a NUMBER variable.
SQLERRM ----Returns character data containing the message associated with
the error number.
一般这样,
EXCEPTION
... WHEN OTHERS THEN
ROLLBACK;
v_error_code:=SQLCODE;
V_ERROR_MESSAGE:=SQLERRM;
INSERT INTO ........
END;
八、调用外围环境
SQLPLUS
PROCEDURE BUILDER
DEVELOPER 2000
OTHER .........
---- 也就是把ERROR NUMBER和MESSAGE输出到SCREEN。
九、使用RAISE_APPLICATION_ERROR
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(-20201,'NO MATCH RECORD YOU WANNA');
END;
当然,RAISE_APPLICATION_ERRO也是可以放在EXECUTE区的,IF 。。。 THEN
RAISE_APPLICATION_ERRO();
END IF;

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=449107
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: