PLSQL新手新手向入门修炼(1)
由于本人对plSQL理解有限,如果文章中出现什么问题,麻烦大家帮我指出来,攻城狮之路,互勉以修远。
本篇文章主要就以下几点来进行展开
(1)plSQL基础写法
(2)plSQL中的控制语句
(3)plSQL中的游标
(4)plSQL意外处理
(5)plSQL存储过程及函数
1.plSQL基础写法
基本格式-匿名块
DECLARE
自定义变量名 类型
BEGIN
SELECT 库中目标变量
INTO 自定义变量
FROM 目标表
WHERE 条件
[EXCEPTION]
异常处理
END;
在编写匿名块的时候有以下几点需要注意的:
1)‘库中目标变量名’与‘自定义变量名’不要相同
2)在声明自定义变量的时候,最好赋予一个初始值,进行初始化,同时在给自定义变量取名的时候要注意场景要求
3)在给自定义变量定义类型的时候
可以使用 %TYPE 来声明与 XX 相同的类型+赋值,如下所示
v_number employees.id%TYPE := &employee_id;
可以使用 %ROWTYPE 来声明某个变量,使得其类型与与某张表的记录或者自定义记录类型保持一致 , 如下所示
根据表的记录
c_record employees%ROWTYPE;
4)在plSQL中 := 是赋值符号, 如果在赋值的同时想要手动输入变量 , 可以使用 &(变量名称) 这样在程序运行的同时就可以先手动输入变量了
5)在plSQL中有些SQL的函数是不能够使用的,如decode函数,分组函数(avg,sum…)等
6)plSQL 中insert , update , delete ,merge 等语句与在 SQL 中操作基本相同,但指定条件的时候,可以通知declare进行定义赋值
2.plSQL中的控制语句
plSQL 中存在除了case 语句的另外一种条件语句 , 即 if .. else …
if conditiion then
statements
else
statements
1.在使用控制语句的时候我们对于null的判断需要加以注意
AND 判断
AND | TRUE | FALSE | NULL |
---|---|---|---|
TRUE | TRUE | FALSE | NULL |
FALSE | FALSE | FALSE | FALSE |
NULL | NULL | FALSE | NULL |
OR 判断
OR | TRUE | FALSE | NULL |
---|---|---|---|
TRUE | TRUE | TRUE | TRUE |
FALSE | TRUE | FALSE | NULL |
NULL | TRUE | NULL | NULL |
NOT 判断
TRUE | FALSE | NULL | |
---|---|---|---|
NOT | FALSE | TRUE | NULL |
一般而言只要记住:
- 当 AND 的时候 true + null –> null
- 当 OR 的时候 FALSE+ null –> null
其他情况则按正常情况去判断.
2.循环控制语句
主要分为以下三种:
1)基本循环
LOOP
statement;
…
EXIT WHEN condition;
END LOOP;
2)Wihle循环
WHILE i < upper_bound LOOP
statement;
…
END LOOP;
3)For循环
FOR i IN lower_bound..upper_bound LOOP
statement;
…
END LOOP;
注意:
结合内存表使用,可以实现类似于数组的遍历
首先内存表的定义为:
TYPE table_name IS TABLE OF
目标类型(可以是自定义,某列%type,某表%ROWTYPE)
INDEX BY BINARY_INTEGER;
通过 index by binary_integer 来当做数组的脚标,以便于遍历
我们来举个栗子:
DECLARE
TYPE STUDENT_TYPE IS TABLE OF STUDENT%ROWTYPE
INDEX BY BINARY_INTEGER;
C_STUDENT STUDENT_TYPE;
V_COUNT NUMBER(7, 2) := 17;
BEGIN
FOR i IN 1 .. V_COUNT LOOP
SELECT *
INTO C_STUDENT(i)
FROM STUDENT
WHERE TO_NUMBER(SUBSTR(STUDENT.STUDENT_NO, 2, 3)) = i;
END LOOP;
FOR i IN C_STUDENT.FIRST .. C_STUDENT.LAST LOOP
DBMS_OUTPUT.PUT_LINE(C_STUDENT(i).STUDENT_NAME);
END LOOP;
END;
这个例子中还存在一个问题,就是说第一个循环的最终变量是手动输入的,我们需要通过表达式来使这个变量呈现动态变化,这个问题的解决方案我会贴在我日后的文章中。
3.plSQL中的游标
游标是plSQL中很重要的一部分,所以务必通过实际验证以校验自己的了解情况如何,游标分为两种类型
- 显式
由程序员声明的游标,对于返回多行结果的SQL语句返回结果,可以使用显式游标独立的处理其中每一行的数据 - 隐式
oracle 通过使用隐式游标来解析和执行我们提交的SQL语句
1)关于隐式存在以下几条有用的属性:(重要)
* SQL%ROWCOUNT 受最近的SQL语句影响的行数
* SQL%FOUND 最近的SQL语句是否影响了一行以上的数据
* SQL%NOTFOUND 最近的SQL语句是否未影响任何数据
* SQL%ISOPEN 对于隐式游标而言永远为false
2)关于显式游标的使用(重要)
有以下几个步骤:
- 声明游标
- 打开游标
- 提取当前行到变量
- 测试行的存在
- 关闭游标
我们来举个栗子:
DECLARE
V_ST_NO STUDENT.STUDENT_NO%TYPE;
V_ST_NAME STUDENT.STUDENT_NAME%TYPE;
CURSOR ST_CURSOR IS
SELECT STUDENT_NO,STUDENT_NAME
FROM STUDENT;
BEGIN
OPEN ST_CURSOR;
LOOP
FETCH ST_CURSOR INTO V_ST_NO , V_ST_NAME;
EXIT WHEN ST_CURSOR%ROWCOUNT > 10 OR ST_CURSOR%NOTFOUND;
DBMS_OUTPUT.put_line(V_ST_NO ||' ' || V_ST_NAME );
END LOOP;
CLOSE ST_CURSOR;
END;
在这段代码中我们将student表中的id,name通过游标将前十条进行了输出,在这之中主要要注意的这段:
FETCH ST_CURSOR INTO V_ST_NO , V_ST_NAME;
–将游标中的值插入到指定自定义变量中
EXIT WHEN ST_CURSOR%ROWCOUNT > 10 OR ST_CURSOR%NOTFOUND;
–通过对最近执行语句条数的判断,从而决定何时跳出循环
我们还可以通过 FOR 循环语句来使用游标,相对之前的这种使用方式写法较为简单,但可读性没有之前这条来的强,但是使用 FOR 循环语句来使用游标在效率上比前一种块上许多
我们来举个栗子:
DECLARE
V_ST_NO STUDENT.STUDENT_NO%TYPE;
V_ST_NAME STUDENT.STUDENT_NAME%TYPE;
BEGIN
FOR ST_CURSOR IN(SELECT STUDENT_NO,STUDENT_NAME
FROM STUDENT) LOOP
V_ST_NO := ST_CURSOR.STUDENT_NO;
V_ST_NAME := ST_CURSOR.STUDENT_NAME;
DBMS_OUTPUT.put_line(V_ST_NO ||' ' || V_ST_NAME );
END LOOP ;
END;
在使用游标的时候我们还可以使用带参数的游标,来缩小范围以提高效率。
在某些情况下我们使用游标是为了更新或者删除一些记录,在这种情况下我们需要对这部分数据进行锁定,此时我们需要在声明的最后面加上一条语句: for update of 目标条件 nowait;
还有一种情况就是由于我们经常要逐条处理游标中的每一条记录,在循环体中做update 或者 delete 是需要有where 指向游标的当前记录,我们可以有更简单的 where 条件写法,我们可以将where条件语句写在游标中,如下:
DECLARE
CURSOR ST_CURSOR IS
SELECT S.STUDENT_NO, S.STUDENT_NAME FROM STUDENT S WHERE TO_NUMBER(SUBSTR(S.STUDENT_NO, 2, 3))< 11 FOR UPDATE OF STUDENT_NO NOWAIT;
BEGIN FOR ST_record IN ST_CURSOR LOOP IF TO_NUMBER(SUBSTR(ST_record.STUDENT_NO, 2, 3)) < 11 THEN UPDATE STUDENT SET STUDENT_NO = ST_record.STUDENT_NO || 'M' WHERE CURRENT OF ST_CURSOR;
END IF;
END LOOP;
END;
4.plSQL意外处理
1)处理预定义例外
在 oracle 中存在一些常见例外,它已经帮我们预定义好了,使用时无需实现声明如:
- ——NO_DATA_FOUND
- ——TOO_MANY_ROWS
- ——INVALID_CURSOR
- ——ZERO_DIVIDE等
栗子如下:
BEGIN ... EXCEPTION WHEN NO_DATA_FOUND THEN statement; WHEN TOO_MANY_ROWS THEN statement; WHEN OTHERS THEN STATEMENT;
END;
我们可以通过使用 SQLCODE , SQLERRM 来分别返回Oracle 的错误号和错误描述,栗子如下:
DECLARE
V_ERROR_CODE NUMBER;
V_ERROR_MESSAGE VARCHAR2(255);
BEGIN .. . EXCEPTION ... WHEN OTHERS THEN ROLLBACK;
V_ERROR_CODE := SQLCODE;
V_ERROR_MESSAGE := SQLERRM;
INSERT INTO ERRORS VALUES (V_ERROR_CODE, V_ERROR_MESSAGE);
END;
一般而言很多情况下例外都是数据库之前没有预声明的,此时就需要我们对这些例外进行声明处理,一般而言我们通过raise语句显示的抛出例外
如果觉得先声明再抛出例外很麻烦,也可以使用 RAISE_APPLICATION_ERROR()函数进行简化处理
如下所示
RAISE_APPLICATION_ERROR(-22202 , ‘This is not a valid manager ‘);
同时,还请注意,当我们在使用匿名块的嵌套的时候,当内层例外没有被处理,会一层一层向外传递,直到外层处理位置,或直接报错
5.plSQL存储过程及函数
这个存储过程及函数的调用是很重要的一块,通过这一块的调用,可以大幅提高代码的复用性,下文会通过我的一些代码以及个人看法来进行介绍:
1)存储过程的定义,我们举个栗子来看看怎么写:
CREATE OR REPLACE PROCEDURE ST_PROCEDURE (ST_NO IN STUDENT.STUDENT_NO%TYPE, ST_NAME OUT STUDENT.STUDENT_NAME%TYPE, ST_GEN IN OUT STUDENT.STUDENT_GENDER%TYPE) IS BEGIN SELECT S.STUDENT_NAME, S.STUDENT_GENDER INTO ST_NAME, ST_GEN FROM STUDENT S WHERE S.STUDENT_NO = ST_NO;
END ST_PROCEDURE;
一般而言大致模板如此,我们需要注意的主要有3点:
- 对于()中的参数有三种关系可以进行书写,IN ,OUT , IN OUT 。
*IN 指的是由运行环境输入至存储过程中
*OUT 指的是由存储过程返回至运行环境
*IN OUT 则是既可以输入,也可以输出 - 存储过程不存在返回值,即RETURN ,但可以输出值
- 对于在存储过程中出现的例外,如果在存储过程中没有解决,则会直接跳出存储过程,至调用存储过程的外部例外处理程序中
2)函数的定义,我们举个栗子来看看怎么写:
CREATE OR REPLACE FUNCTION ST_FUNCTION (ST_NO IN STUDENT.STUDENT_NO%TYPE) RETURN VARCHAR2 IS V_NAME VARCHAR2(200);
BEGIN SELECT S.STUDENT_NAME INTO ST_NAME FROM STUDENT S WHERE S.STUDENT_NO = ST_NO;
V_NAME := ST_NAME;
RETURN(V_NAME);
END ST_FUNCTION;
函数的使用范围很广,因为其拥有返回值,其返回值可以是单独的值,也可以是一个表对象,所以在使用的时候存在以下几点需要注意的:
(1)函数只能被调用,与存储关系不同的,存储关系可以单独进行运行,但是函数只能在语句中被调用;
(2)函数中只能存在 IN 模式的参数;
(3)只能接收或者返回SQL类型的数据,不能接收plSQL特有的参数比如recored ,plSQL内存表等;
(4)在SQL语句中使用函数,函数中不能包含DML语句,事务结束语句等;
(5)在UPDATE / DETELE 语句中调用函数,函数中不能存在针对该表的select 语句。