一、PL/SQL块的词法

PL/SQL中可以使用字母、数字、空格和特殊符号等,主要包括:

标识符——变量、关键字、保留字

分隔符——分号,逗号,加号或减号等

常量——字符串,数字,布尔型变量

注释——单行注释、多行注释

编写PL/SQL程序时,需要注意以下两点:

1. 字符和日期必须使用单引号引用,单引号中间可以引用双引号。

2. 数字可以是单独的数字,也可以使科学计数法。

为了养成良好的编程,最好使用tab键对代码进行缩进,有关键字的行回车另起一行,以使程序更加易读,逻辑结构更加清晰。

二、注释

PL/SQL和其他编程语言一样,也经常需要用到注释。注释部分程序执行时会自动忽略。它的注释主要有两种:

1. 单行注释:使用-- 表示,注释的范围仅限于-- 所在的行;

2. 多行注释:使用/* */表示,注释的范围是/* */所包含的所有行。

如下面这个例子,就同时用到了单行注释和多行注释:

SQL> edit

DECLAREv_annual_salNUMBER(9,2);monthly_salNUMBER;BEGIN/*Computetheannualsalarybasedonthemonthlysalaryinputfromtheuser*/--使用了多行注释monthly_sal:=3000;v_annual_sal:=monthly_sal*12;--Thefollowinglinedisplaystheannualsalary--使用了单行注释DBMS_OUTPUT.PUT_LINE(v_annual_sal);END;/

SQL> /

36000PL/SQLproceduresuccessfullycompleted.

三、SQL函数在PL/SQL中的应用

PL/SQL和SQL是无缝衔接的,因此SQL的很多函数可以在PL/SQL中直接引用,比如SQL中的单行函数可以直接使用;但是一些GROUP函数和DECODE函数则无法在PL/SQL中使用。不过PL/SQL有自己的条件控制语句,可以做更为灵活的操作。

下面举几个SQL函数在PL/SQL中使用的例子:

SQL> edit

DECLAREv_desc_sizeINTEGER(5);v_prod_descriptionVARCHAR2(70):='Youcanusethisproductwithyourradiosforhigherfrequency';BEGINv_desc_size:=LENGTH(v_prod_description);--这里使用了SQL的函数LENGTH()DBMS_OUTPUT.PUT_LINE(v_desc_size);END;/

SQL> /

62PL/SQLproceduresuccessfullycompleted.

需要注意的是,Oracle11g之前,PL/SQL中使用递增函数(Sequences)的方法是:

DECLAREv_new_idNUMBER;BEGINSELECTmy_seq.NEXTVALINTOv_new_idFROMDual;--需要写成完整的形式END;

但是在Oracle11g之后,递增函数简化成如下的形式:

DECLAREv_new_idNUMBER;BEGINv_new_id:=my_seq.NEXTVAL;--可以写成简化的形式了END;

四、数据类型的转换

PL/SQL中要进行转换的数据必须是相似的,比如数字123和字符串’123’是可以互相转换的,但是’abc’则不能转换为数字。

PL/SQL中数据类型的转换主要分两类:

1. 隐式转换——隐式转换是指数据会自动转换。但是需要注意的是,自动转换受制于NLS环境变量,如果环境变量没有进行相应的配置,则有可能无法转换,因此不推荐这种方式。

下面看一个数据类型转换的例子:

SQL> edit

DECLAREa_numberNUMBER;BEGINa_number:='125';--'125'是个字符串a_number:=a_number+3;--'125'自动转换成了数字DBMS_OUTPUT.PUT_LINE(to_char(a_number,'9999'));--使用了to_char这个函数进行显示转换END;/

SQL> @notes/s1.sql

128PL/SQLproceduresuccessfullycompleted.

2. 显式转换——使用专用的函数来转换。如TO_CHAR,TO_DATE, TO_NUMBER, TO_TIMESTAMP等函数。

如要进行日期转换:

SQL> edit

DECLAREv_date_of_joiningDATE;BEGINv_date_of_joining:=TO_DATE('February02,2000','MonthDD,YYYY');--使用TO_DATE这个函数进行显式转换DBMS_OUTPUT.PUT_LINE(v_date_of_joining);END;/

SQL> /

02-FEB-00PL/SQLproceduresuccessfullycompleted.

五、块的嵌套

A. 嵌套块可以放的位置:

PL/SQL可以嵌套其他的块,被嵌套的块可以放在以下两个部分:

1. 可执行部分,即BEGIN处;

2. 异常处理部分,即EXCEPTION部分。

需要注意的是,嵌套块最多不要超过3层。

B. 变量的范围(SCOPE):

内层嵌套块可以引用外层块中声明的变量;

但是外层块不可以引用内层块中声明的变量。

下面来看一个变量引用范围的例子:

SQL> edit

DECLAREv_outer_variableVARCHAR2(20):='GLOBAL_VARIABLE';--声明在外层块中的变量BEGINDECLAREv_inner_variableVARCHAR2(20):='LOCALVARIABLE';--声明在内层块中的变量BEGINDBMS_OUTPUT.PUT_LINE(v_inner_variable);--内层块中的函数可以引用内层块声明的变量DBMS_OUTPUT.PUT_LINE(v_outer_variable);--内层块的函数也可以引用外层块声明的变量END;DBMS_OUTPUT.PUT_LINE(v_outer_variable);--外层块只能引用声明在外层块的变量END;

SQL> /

LOCALVARIABLEGLOBAL_VARIABLEGLOBAL_VARIABLEPL/SQLproceduresuccessfullycompleted.

如果修改一下,在外层块里引用一下内层块的变量:

SQL> edit

DECLAREv_outer_variableVARCHAR2(20):='GLOBAL_VARIABLE';BEGINDECLAREv_inner_variableVARCHAR2(20):='LOCALVARIABLE';BEGINDBMS_OUTPUT.PUT_LINE(v_inner_variable);DBMS_OUTPUT.PUT_LINE(v_outer_variable);END;DBMS_OUTPUT.PUT_LINE(v_outer_variable);DBMS_OUTPUT.PUT_LINE(v_inner_variable);END;/

SQL> /

DBMS_OUTPUT.PUT_LINE(v_inner_variable);*ERRORatline11:ORA-06550:line11,column23:PLS-00201:identifier'V_INNER_VARIABLE'mustbedeclaredORA-06550:line11,column2:PL/SQL:Statementignored--报错了,说明无法引用内层块声明的变量


C.变量的可见性(visibility)

如果声明变量时出现了重名的情况,则采用就近原则,优先采用本地的变量,而在外层块中的变量会被覆盖(invisibility)。

下面来看一个变量名重名的例子:

SQL> edit

--ExampleaboutvariablescopeandvisibilityinnestblocksDECLAREv_father_nameVARCHAR2(20):='Patrick';v_date_of_birthDATE:='20-Apr-1972';--定义了父亲的名字和生日BEGINDECLAREv_child_nameVARCHAR2(20):='Mike';v_date_of_birthDATE:='12-Dec-2002';--定义了儿子的名字和生日BEGINDBMS_OUTPUT.PUT_LINE('Father''sName:'||v_father_name);DBMS_OUTPUT.PUT_LINE('DateofBirth:'||v_date_of_birth);--这里引用了父亲的名字--但是根据就近原则,引用了儿子的生日DBMS_OUTPUT.PUT_LINE('Child''sName:'||v_child_name);--引用了儿子的名字END;DBMS_OUTPUT.PUT_LINE('DateofBirth:'||v_date_of_birth);--内层块执行结束后内存被释放,此时引用的是父亲的生日END;/

SQL> /

Father'sName:PatrickDateofBirth:12-DEC-02--父亲的名字,儿子的生日Child'sName:MikeDateofBirth:20-APR-72--儿子的名字,父亲的生日PL/SQLproceduresuccessfullycompleted.

D.使用限定词标签引用变量

如果要内层块一定要引用外层块的变量,在声明时使用标签,引用时指定标签作为修饰语,则可以避免变量覆盖的情况。

看下面这组例子:

SQL> edit

BEGIN<<outer>>--使用了标签<<outer>>DECLAREv_father_nameVARCHAR2(20):='Patrick';v_date_of_birthDATE:='20-Apr-1972';BEGINDECLAREv_child_nameVARCHAR2(20):='Mike';v_date_of_birthDATE:='12-Dec-2002';BEGINDBMS_OUTPUT.PUT_LINE('Father''sName:'||v_father_name);DBMS_OUTPUT.PUT_LINE('DateofBirth:'||outer.v_date_of_birth);--引用变量时指明该变量是外层块中的变量。DBMS_OUTPUT.PUT_LINE('Child''sName:'||v_child_name);DBMS_OUTPUT.PUT_LINE('DateofBirth:'||v_date_of_birth);--这里引用的变量是内层块的变量END;END;ENDouter;

SQL> /

Father'sName:PatrickDateofBirth:20-APR-72Child'sName:MikeDateofBirth:12-DEC-02--这一次父亲的名字和生日、孩子的名字和生日能够匹配了PL/SQLproceduresuccessfullycompleted.

当然,也可以同时使用outer和inner标签

SQL> edit

BEGIN<<outer>>--使用outer标签DECLAREv_father_nameVARCHAR2(20):='Patrick';v_date_of_birthDATE:='20-Apr-1972';BEGIN<<inner>>--使用inner标签DECLAREv_child_nameVARCHAR2(20):='Mike';v_date_of_birthDATE:='12-Dec-2002';BEGINDBMS_OUTPUT.PUT_LINE('Father''sName:'||v_father_name);DBMS_OUTPUT.PUT_LINE('DateofBirth:'||outer.v_date_of_birth);--引用时指明是outer的变量DBMS_OUTPUT.PUT_LINE('Child''sName:'||v_child_name);DBMS_OUTPUT.PUT_LINE('DateofBirth:'||inner.v_date_of_birth);--引用时指明是inner的变量END;END;ENDouter;/

SQL> /

Father'sName:PatrickDateofBirth:20-APR-72Child'sName:MikeDateofBirth:12-DEC-02--和上一次的执行结果是一致的PL/SQLproceduresuccessfullycompleted.

限定词除了可以明确指定外,还可以使用包名、Procedure的名字。使用限定词来引用变量,是一个很好的编程习惯,可以防止混淆和冲突。

六、PL/SQL中的运算符

PL/SQL中的运算符主要包括以下几类:

1. 逻辑运算符:如AND、OR 和NOT

2. 算术运算符:如+、-、*、/和指数运算符 **

3. 连接符,将多个字符串连起来,如||

PL/SQL的运算规则和SQL一致,如*和/的优先级高于+和-等等,出于安全性考虑,可以通过括号来强制控制运算的顺序

七、PL/SQL编程的通用规则

进行PL/SQL编程,建议遵循以下通用规则:

1. 为代码加注释;

2. 统一大小写的规则(关键字大写,自己定义的变量小写)

3. 确定命名规则,如声明变量时以v_开头

4. 通过缩减增强可读性,很多开发工具集成了格式化的工具,可以自动完成缩减

(在vim 中,可以通过set autointent 设定自动缩进,settabstop=#设定缩进量,其中#表示要缩进的字符数)

5. 分行写代码,使代码更易读。