1、PL/SQL块结构
PL/SQL最小有意义的代码单元叫做块(block)。一个块就是一组代码,这个块给出了执行边界,也为变量声明和异常处理提供了作用范围。PL/SQL允许我们创建匿名块和命名块。命名块可以是包、过程、函数、触发器或者对象类型。
一个PL/SQL块最多可以由4个不同单元组成,只有一个单是必不可少的(执行单元)。
块头(header) 只有命名块才会有这个单元。块头确定命名块获程序的调用方式,这个单元是可选的。
声明单元(declaration section) 定义变量、游标、以及可以被后面执行单元或异常单元引用的子块单元。这个单元也是可选的
执行单元(execution section) 这一部分包含要执行的业务逻辑,这个单元式必须的
异常处理单元(exception section) 处理抛出的异常,这个单元式可选的
例如:插入指定的名称到employee且插入一个日期
PROCEDURE get_happy (ename_in IN VARCHAR2) -- 块头 IS l_hiredate DATE; -- 声明单元 BEGIN l_hiredate := SYSDATE - 2; INSERT INTO employee (emp_name, hiredate) VALUES (ename_in, l_hiredate); -- 执行单元 EXCEPTION WHEN DUP_VAL_IN_INDEX THEN -- 异常处理单元 DBMS_OUTPUT.PUT_LINE('Cannot insert.'); END;
2、匿名块
匿名块没有一个可以引用的句柄,因此不能被其他块调用。相反匿名块可以调用其他块。匿名块直接以DECLARE或BEGIN开始。如:
BEGIN DBMS_OUTPUT.PUT_LINE('Hello World'); END;
一个匿名PL/SQL代码块的语法通常是这样:
[DECLARE . . . 声明语句 . . . ] BEGIN . . . 一个或多个执行语句. . . [EXCEPTION . . . 异常处理语句] END;
a、最小匿名块
BEGIN DBMS_OUTPUT.PUT_LINE(SYSDATE); END;
b、有声明单元
DECLARE now_date VARCHA2(9); BEGIN now_date := SYSDATE; DBMS_OUTPUT.PUT_LINE( now_date); END;
c、存在异常处理块
DECLARE now_date VARCHA2(9); BEGIN now_date := SYSDATE; DBMS_OUTPUT.PUT_LINE( now_date); EXCEPTION WHEN VALUE_ERROR THEN DBMS_OUTPUT.PUT_LINE('now_date is too small'); END;
3、命名块
命名块和匿名块最大区别在于,命名块有头部信息。一个过程的头部是这样的:
PROCEDURE [schema.] name [(parameter [, parameter . . . ])] [AUTHID {DEFINER | CURRENT_USER}]
一个函数的头部的语法类似,但是有RETURN关键字:
FUNCTION [schema.] name [(parameter [, parameter . . . ])] RETURN return_datatype [AUTHID {DEFINER | CURRENT_USER}] [DETERMINISTIC] [PARALLEL ENABLE . . . ] [PIPELINED [USING . . . ] | AGGREGATE USING . . .]
例如:创建一个hello过程,显示'Hello World'。
PROCEDURE hello IS v_str VARCHAR2(100); BEGIN v_str := 'Hello World'; DBMS_OUTPUT.PUT_LINE(v_str ); END;
4、嵌套块
PL/SQL允许块中再嵌套块。被嵌套的块还有其他的叫法比如闭包块、子块或者sub-block如。外层的PL/SQL块可以叫包围块或者父块。
PROCEDURE hello IS v_str VARCHAR2(100); BEGIN v_str := 'Hello World'; /* 嵌套模块开始 */ DECLARE v_str2 BEGIN v_str2 := v_str || ' inner block'; DBMS_OUTPUT.PUT_LINE(v_str2); END; /* 嵌套模块结束 */ DBMS_OUTPUT.PUT_LINE(v_str ); END;
5、作用范围
在PL/SQL中,变量、异常、模块和一些其他的结构都是属于声明它们的块局部。当这个块结构结束后,我们就再也无法引用这些结构中声明的变量。
6、规范SQL语句中所有对变量和列的引用
-- 包头 PACKAGE scope_demo IS g_global NUMBER; PROCEDURE set_global (number_in IN NUMBER); END scope_demo; -- 包体 PACKAGE BODY scope_demo IS PROCEDURE set_global (number_in IN NUMBER) IS l_salary NUMBER := 10000; -- 只能在set_global作用域中 l_count PLS_INTEGER; BEGIN -- 嵌套模块 <<local_block>> -- 为嵌套模块命名 DECLARE l_inner PLS_INTEGER; -- 只能在local_block嵌套块中使用 BEGIN SELECT COUNT(*) INTO set_global . l_count FROM employees e WHERE e.department_id= local_block.l_inner AND e.salary > set_global.l_salary; END local_block; scope_demo.g_global := set_global.number_in; END set_global; END scope_demo;
注意:在嵌入式SQL语句中,Oracle数据库会首先把这些不规范的标示符当作表中的列名来解释,如果不能找到匹配的列,Oracle会接着尝试把它当作作用范围内的PL/SQL变量来解释。
7、可见性
可见性表示我们是否可以仅通过名字引用一个变量,或者还需要在名字的前面加一个前缀。
a、可见的标示符
先观察一个例子:
declare age NUMBER(2); name VARCHAR2(100); begin age := 22; name := 'zhangsan'; end;
因为 age 和 name 两个变量的声明和使用是再同一个块中,我们就可以非常方便的通过“非规范化”的标示符引用它们,这些标示符也被称为可见标示符。一个可见标示符实践上可能是这样的:
1)一个在当前块中声明的标示符
2)一个在包含当前块的块中声明的标示符
3)属于你所有的一个单独的数据库对象或者 PL/SQL 对象
4)一个你能看见的数据库别名,而且你对别名指向的数据库对象或者 PL/SQL 对象有适当的权限
5)一个循环体中的索引变量(但这个变量的作用域只在循环体中)
通过模块的名称来规范化标识符的名字,PL/SQL 提供许多方法来规范化一个标志符以保证对于标示符的引用能够被正确的解析。比如,使用包我们可以创建属于包的全局作用域变量。
PACKAGE comany_pkg IS name varchar2(200); END comany_pkg;
我们可以通过 comany_pkg.name 的形式访问到该变量。
PL/SQL 还提供了一个特别重要的特性叫做嵌套程序。嵌套程序就是完全出现在包围块的声明单元的过程获函数。重要的是,嵌套程序可以引用外层块声明的变量及参数,如下:
PROCEDURE outer_proc (fudge_factory_in IN NUMBER) IS subtotal NUMBER := 0; /* 嵌套块的开始,注意过程的全部内容都放在outer_proc 的声明单元 */ PROCEDURE compute_running(increment_in IN PLS_INTEGER) IS BEGIN /* subtotal,是再上面申明的,即在作用范围内同时也是可见的 */ subtotal := subtotal + increment_in * fudge_factory_in ; END; /* 嵌套块的结束*/ BEGIN FOR month_idx IN 1..12 LOOP /* 调用compute_running嵌套程序 */ compute_running(month_idx ); END LOOP; DBMS_OUTPUT.PUT_LINE('Fudged total for year: ' || subtotal); END;
8、NULL
在 Oracle 数据库中,一个值的缺失是通过关键字 NULL 来表示的。
在 Oracle SQL 和 PL/SQL 中,一个空字符串和零个字符的直接量是没有区别的,后缀表现为''(两个连载一起的单引号,之间没有任何字符)。例如,下面表达式在SQL还是PL/SQL中结果都是TRUE。
'' IS NULL;
在PL/SQL中给VARCHAR2(n)变量赋值一个长度为0的字符串,导致结果就是NULL;
DECLARE str VARCHAR2(0); BEGIN IF str IS NULL THEN -- 结果是TURE
CHAR数据类型有些怪异,如果在PL/SQL中创建CHAR(n),然后将n=0,数据库会用空格字符填满这个变量,因为这个变量不为空:
DECLARE flag CHAR(2) := ' '; BEGIN IF flag = ' ' . . . -- 结果为TRUE IF flag IS NULL . . . -- 结果是FALSE
9、布尔直接量
PL/SQL提供了两个表示布尔值的直接量:TRUE 和 FALSE。这不是字符串,我们不用加引号。可以直接赋值给布尔类型的变量。如:
DECLARE flag BOOLEAN; BEGIN flag := TRUE; IF flag HTEN . . . END IF; END;
一个布尔表达式、变量、或者常量也可以是 NULL,这既不是 TRUE 也不是 FALSE。
10、分号分隔符
一个PL/SQL程序是由一系列的声明和语句组成。这些都是通过逻辑定义的,而不是物理定义的。换句话说,不是根据每一个物理行的结尾为结束的,而是用分号(;)结束的。如:
IF salary < 1000 THEN salary := salary + salary * 0.25; END IF;
或
IF salary < 1000 THEN salary := salary + salary * 0.25; END IF; -- 不推荐
11、注释
PL/SQL 的注释分为“单行注释”和“多行注释”。语法如下:
-- 声明单行注释 /* 声明多汗注释 */
12、PRAGMA关键字
一个真正来自于希腊语的编程概念就是pragma,这意味着“契约”或者,暗示的是,一个“动作”。在许多编程语言中,pragma通常是一行用于告诉编译器采取什么行动的源代码。就像提供给编译器一个选项;可以导致不同的运行时刻行为,但又不会直接被翻译成字节代码。PL/SQL语法:
PRAGMA instruction_to_compiler PL/SQL编译器能够接受出现在声明单元的这种指令。不过许多指令还有额外的相关配置需求。
AUTONOMOUS_TRANSACTION 这个指令告诉PL/SQL运行引擎,当前块对数据库所作的修改提交或者回滚不影响主事务或者外层事务。
EXCEPTION_INIT 这个指令告诉编译器把一个特殊的错误号和我们在程序中声明的异常标示符关联起来。必须跟在异常声明之后。
RESTRICT_REFERENCES 告诉编译器一个包程序的纯度级别(避免副作用)。
SERIALLY_REUSABLE 告诉PL/SQL运行时引擎,包级别的数据在引用之间不应该保留。
如:EXCEPTION_INIT 的用法:
DECLARE my_exception EXCEPTION; PRAGMA EXCEPTION_INIT( my_exception, -2289); BEGIN . . . EXCEPTION WHEN my_exception THEN . . . END;
13、标签
PL/SQL标签是一种为我们程序中特定部分命名的方法。语法:<<identifier>>。其中identifier是一个有效的PL/SQL标识符(最大长度可以到30个字符,并用字母开头)。不需要终结符号;可以直接放到其他标注的内容前面,这些内容必须是一个可执行的语句,就算是一个NULL语句:
BEGIN . . . <<the_scpot>> NULL;
因为匿名块本身是可以执行的语句,一个标签就可以在匿名块的执行周期为其命名。如:
<<test>> BEGIN . . . EXCEPTION . . . END test;
使用标签可以从当前、内嵌的代码中,规范的引用来自外层包围块的具有相同名字的元素。如:
<<out_block>> DECLARE count NUMBER(4) := 100; BEGIN /* 内部嵌套块*/ DECLARE count NUMBER(4) := 1; BEGIN IF count = out_block.count THEN . . . END IF; END; END;
以上如果不使用标签,则无法区分count到底是外围还是嵌套块的count。
标签还可以作为GOTO语句的目标。如:
DECLARE . . . BEGIN . . . GOTO label_name . . . <<label_name>> . . . END;