一、PL/SQL简介

1、PL/SQL块结构

[DECLARE]

--声明部分,可选

BEGIN

--执行部分,必须

[EXCEPTION]

--一场处理部分,可选

END

(1)声明部分由关键字DECLARE开始,到BEGIN结束。在这部分可以声明PL/SQL程序块中所用到的变量、常量和游标。需要注意的是:在某个PL/SQL块中声明的内容只能在当前块中使用,而在其他PL/SQL块中是无法引用的。

(2)执行部分以BEGIN开始,它的结束方式通常有两种。如果PL/SQL块中的代码在运行时出现异常,则执行完异常处理部分的代码就结束;如果没有使用异常处理或PL/SQL块未出现异常,则以关键字END结束。

(3)异常处理部分以关键字EXCEPTION开始,在该关键字所包含的代码执行完毕后,整个PL/SQL块就结束了。对于可能出现的多种异常情况,用户可以使用WHEN  THEN语句来实现多分支判断,然后再每个分支下通过编写代码来处理相应的异常;

(4)对于PL/SQL块中的语句,需要指出的是:每一条语句必须以分号结束,每条SQL语句可以写成多行的形式,同样必须使用分号来结束。另外,一行中也可以有多条SQL语句,但是他们之间必须以分号隔开。

2、代码注释和标识符

(1)单行注释

单行注释由两个连接字符--开始,后面紧跟这注释内容;

(2)多行注释

多行注释由/*开头,以*/结尾,这种多行注释的方法再大多数的编程语言中是相同的。

 

二、数据类型、变量和常量

1、基本数据类型

(1)  数值类型

数值类型主要包括:NUMBER、PLS_INTEGER和BINARY_INTEGER三种类型;

(2)字符类型

字符类型主要包括VARCHAR2、CHAR、LONG、NCHAR和NVARCHAR2等;这些类型的变量用来存储字符串或者字符数据。

(3)日期类型

日期类型只有一种类型,用来存储日期和时间信息,DATE类型的存储空间是7个字节,分别使用一个字节存储世纪、年、月、天、小时、分钟和秒。

(4)布尔类型

布尔类型也只有一种——即BOOLEAN,主要用于程序的流程控制和业务逻辑判断,其变量值可以是TRUE、FALSH或NULL中的一种。

2、特殊数据类型

(1) %TYPE类型

使用%TYPE关键字可以声明一个与指定列明相同的数据类型,它通常紧跟再指定列明的后面

示例1:声明一个与表EMP中job列相同的数据类型。

SQL> declare 
           var_job emp.job%type;

示例2:使用%TYPE类型的变量输出EMP表中编号为7369的员工名称和职务信息。

set serveroutput on                                                            --在服务端输出
declare
  var_ename emp.ename%type;                                          --声明与ename列类型相同的变量
  var_job emp.job%type;                                                      --声明与job列类型相同的变量
begin
  select ename,job
  into var_ename,var_job
  from emp
  where empno=7369;                                                         --检索数据,并保存在变量中
  dbms_output.put_line(var_ename||'的职务是'||var_job);   --输出变量的值
end;
/

(2)RECORD类型

RECORD类型也称作记录类型,使用该类型的变量可以存储由多个列值组成的一行数据。在声明记录类型前,首先需要定义记录类型,然后才可以声明记录类型的变量。记录类型是一种结构化的数据类型,它使用TYPE语句进行定义,在记录类型的定义结构中包含成员变量及其数据类型,其语法格式如下:

type record_type is record 
(
     var_member1 data_type [not null] [:=default_value,
     ...
     var_membern data_type [not null] [:=default_value])

record_type :表示要定义的记录类型名称;

var_member1:表示该记录类型的成员变量名称;

data_type:表示成员变量的数据类型。

示例1:声明一个记录类型emp_type,然后使用该类型的变量存储emp表中的一条记录信息,并输出这条记录信息;

set serveroutput on                                            --输出到服务端
declare
  type emp_type is record                                    --声明record类型emp_type
  (
    var_ename varchar2(20),                                   --定义字段
    var_job varchar2(20),
    var_sal number
  );
  empinfo  emp_type;                                             --定义变量
begin
  select ename,job,sal
  into empinfo
  from emp
  where empno=7369;                                           --检索数据

  dbms_output.put_line('雇员'||empinfo.var_ename||'的职务是'||empinfo.var_job||'、工资是'||empinfo.var_sal);                                          --输出雇员信息
end;
/

(3)%ROWTYPE类型

%ROWTYPE类型的变量结合了%TYPE类型和记录类型变量的优点,它可以根据数据表中行的结构定义一种特殊的数据类型,用来存储从数据表中检索出的一行数据,他的语法形式如下:

rowVar_name table_name%ROWTYPE;
rowVar_name:表示可以存储一行数据的变量名;
table_name:指定的表名;

示例1:声明一个%ROWTYPE类型的变量rowVar_emp,然后使用该变量存储emp表中的一行数据;

set serveroutput on                                                 --输出到服务端
declare
  rowVar_emp emp%rowtype;                                   --定义能够存储emp表中一行数据的变量rowVar_emp
begin
  select * 
  into rowVar_emp
  from emp
  where empno=7369;--检索数据

  dbms_output.put_line('雇员'||rowVar_emp.ename||'的编号是'||rowVar_emp.empno||',职务是'||rowVar_emp.job);   /*输出雇员信息*/end;
/

3、定义变量和常量

(1) 定义变量

<变量名> <数据类型> [(长度) :=<初始值>] ;

(2) 定义常量

<常量名> constant <数据类型> :=<常量名> ;

 

三、流程控制语句

1、选择语句

(1) IF...THEN语句

IF...THEN语句是选择语句中最简单的一种形式,它只做一种情况和条件的判断,其语法如下

IF<condition_expression> THEN
plsql_sentence
END IF;

condition_expression:表示一个表达式,当其值为TRUE时,程序会执行IF下面的PL/SQL语句(即plsql_sentence语句);如果其值为FLASE,则程序会跳过IF下面的语句而直接执行END IF 后面的语句。

plsql_sentence:当condition_expression表达式的值为TRUE时,要执行的PL/SQL语句。

示例1:定义两个字符串变量,然后赋值,接着使用IF...THEN语句比较两个字符串变量的长度,并输出比较结果。

set serveroutput on            --输出到服务端
declare
  var_name1 varchar2(50);  --定义变量1
  var_name2 varchar2(50);  --定义变量2
begin
  var_name1:='East';             --为变量1赋值
  var_name2:='xiaoke';          --为变量2赋值
  if length(var_name1) < length(var_name2) then  --比较两个字符串的长度大小
    dbms_output.put_line('字符串“'||var_name1||'”的长度比字符串“'||var_name2||'”的长度小');
  end if;
end;
/

(2) IF...THEN...ELSE

IF...THEN...ELSE语句是最常用到的一种选择语句,它可以实现判断两种情况,只要IF后面的变大时为FALSE,程序就会执行ELSE语句下面的PL/SQL语句。其语法格式如下:

IF<condition_expression> THEN
plsql_sentence1;
ELSE
plsql_sentence2
END IF;

condition_expression:表示一个表达式,当其值为TRUE时,程序会执行IF下面的PL/SQL语句(即plsql_sentence1语句);否则,程序将执行ELSE下面的OL/SQL语句,即plsql_sentence2语句。

plsql_sentence1:IF语句的表达式值为TRUE时,要执行的PL/SQL语句;

plsql_sentence2:IF语句的表达式值为FLASE时,要执行的PL/SQL语句。

(3) IF...THEN...ELSIF语句

IF...THEN...ELSIF语句实现了多分支判断选择,它使程序的判断选择条件更加丰富,更加多样化。该语句中的哪个判断分支的表达式为TRUE,那么程序就会执行其下面对应的PL/SQL语句,其语法格式如下:

IF<condition_expression1> THEN
plsql_sentence_1;
ELSIF <condition_expression2> THEN
plsql_sencence_2;
...
ELSE
plsql_sentence_n;
END IF;

condition_expression_1:第一个表达式,若其值为FLASE,则程序继续判断condition_expression_2;

condition_expression_2:第二个表达式,若其值为FLASE,则程序继续判断下面的ELSIF语句后面的表达式,若再没有ELSIF语句,则程序将执行ELSE语句下面的PL/SQL语句;

plsql_sentence_1:第一个条件表达式的值为TRUE时,要执行的PL/SQL语句;

plsql_sentence_2:第二个条件表达式的值为TRUE时,要执行的PL/SQL语句;

plsql_sentence_n:当其上面所有的条件表达式的值都为FLASE时,要执行的PL/SQL语句。

示例1:指定一个月数值,然后使用IF...THEN...ELSIF语句判断它所属的季节,并输出季节信息。

set serveroutput on                                 ----输出到服务端
declare
  month int:=10;                                         --定义整形变量并赋值
begin
  if month >= 0 and month <= 3  then          --判断春季
    dbms_output.put_line('这是春季');
  elsif  month >= 4 and month <= 6 then     --判断夏季
    dbms_output.put_line('这是夏季');
  elsif  month >= 7 and month <= 9  then     --判断秋季
    dbms_output.put_line('这是秋季');
  elsif  month >= 10 and month <= 12 then  --判断冬季
    dbms_output.put_line('这是冬季');
  else
    dbms_output.put_line('对不起,月份不合法!');
  end if;
end;
/

(4) CASE语句

CASE语句的执行方式与IF...THEN...ELSIF语句十分相似。在CASE关键字的后面有一个选择器,它通常是一个变量,程序就从这个选择器开始执行,接下来是WHEN子句,并且在WHEN关键字的后面是一个表达式,程序将根据选择器的值去匹配每个WHEN子句中的表达式的值,从而实现执行不同的PLSQL语句的功能,其语法格式如下:

CASE <selector>
     WHEN <expression_1> THEN plsql_sentence_1;
     WHEN <expression_1> THEN plsql_sentence_2;
...
     WHEN <expression_n> THEN plsql_sentence_n;
END CASE;

selector :一个变量,用来存储要检测的值,通常称为选择器。该选择器的值需要与WHEN子句中的表达式的值进行匹配。

expression_1:第一个WHEN子句中的表达式,它通常是一个常量,当选择器的值等于该表达式的值时,程序将执行plsql_sentence_1语句;

expression_2:第二个WHEN子句中的表达式,它通常也是一个常量,当选择器的值等于该表达式的值时,程序将执行plsql_sentence_1语句;

expression_n:第n个WHEN子句中的表达式,它通常也是一个常量,当选择器的值等于该表达式的值时,程序将执行plsql_sentence_n语句;

plsql_sentence:一个PL/SQL语句,当没有与选择器匹配的WHEN常量时,程序执行该PL/SQL语句,其所在的EKSE语句是一个可选项;

示例1:指定一个季度数值,然后使用CASE语句判断它所包含的月份信息并输出。

set serveroutput on                                   --输出到服务端
declare
  season int:=3;                                            --定义整形变量并赋值
  aboutInfo varchar2(50); 
begin
  case season 
    when 1 then
      aboutInfo := season||'季度包括1,2,3月份';
    when 2 then
      aboutInfo := season||'季度包括4,5,6月份';
    when 3 then
      aboutInfo := season||'季度包括7,8,9月份';
    when 4 then
      aboutInfo := season||'季度包括10,11,12月份';
    else
      aboutInfo := season||'季节不合法';
  end case;
  dbms_output.put_line(aboutinfo);             ---输出结果
end;
/

2、循环语句

(1) LOOP语句

LOOP语句会先执行一次循环体,然后判断EXIT WHEN关键字后面的条件表达式的值是TRUE还是FLASE,如果是TRUE,则程序会推出循环体,否则程序将再次执行循环体,这样就使得程序至少能够执行一次循环体,其语法格式如下:

LOOP
    plsql_sentence;
    EXIT WHEN end_condition_exp
END LOOP;

plsql_sentence:循环体中的PL/SQL语句,可能是一条语句,也可能是多条,这是循环体的核心部分,这些PL/SQL语句至少会被执行一遍。

end_condition_exp:循环结束条件表达式,当该表达式的值为TRUE时,程序会退出循环体,否则程序将再次执行循环体。

(2) WHILE语句

WHILE语句根据它的条件表达式的值执行零次或多次循环体,在每次执行循环体之前,首先要判断条件表达式的值是否为TRUE,若为TRUE,则程序执行循环体;否则推出WHILE循环,然后继续执行WHILE语句后面的其他代码,其语法格式如下:

WHILE condition_expression LOOP
    plsql_sentence;
END LOOP;

condition_expression:表示一个条件表达式,当其值为TRUE时,程序执行循环体,否则程序退出循环体,程序每次执行循环体之前,都要首先判断该表达式的值是否为TRUE。

plsql_sentence:循环体内的PLSQL语句。

示例1:使用WHILE语句球的前100个自然数的和,并输出结果。

set serveroutput on
declare
  sum_i int:= 0;                                   --定义整数变量,存储整数和
  i int:= 0;                                            --定义整数变量,存储自然数
begin
  while i<=99 loop
    i:=i+1;--得出自然数
    sum_i:= sum_i+i;--计算前n个自然数的和
  end loop;
  dbms_output.put_line('前100个自然数的和是:'||sum_i);--计算前100个自然数的和
end;
/

(3) FOR语句

FOR语句时一个可预置循环次数的循环控制语句,它有一个循环计数器,通常是一个整型变量,通过这个循环计数器来控制循环执行的次数。该计数器可以从小到大进行记录,也可以相反,从大到小进行记录。另外,该计数器值的合法性由上限值和下限值控制,托计数器值在上限值和下限值的范围内,则程序执行循环,否则,种植循环,其语法格式如下:

FOR variable_counter_name in [REVERSE] lower_limit...upper_limit LOOP
plsql_sentence;
END LOOP;

variable_counter_name:表示一个变量,通常为整数类型,用来作为计数器。默认情况下计数器的值会循环递增,当在循环中使用REVERSE关键字时,计数器的值会随循环递减。

lower_limit:计数器的下限值,当计数器的值小于下限值时,程序终止FOR循环;

upper_limit:计数器的上限值,当计数器的值大于上限值时,程序终止FOE循环;

plsql_sentence:表示PL/SQL语句,作为FOE语句的循环体。

示例1:使用FOR语句求得前100个自然数中偶数之和,并输出结果;

set serveroutput on
declare
  sum_i int:= 0;                               --定义整数变量,存储整数和
begin
  for i in reverse 1..100 loop           --遍历前100个自然数
    if mod(i,2)=0 then                        --判断是否为偶数
      sum_i:=sum_i+i;                          --计算偶数和
    end if;
  end loop;
  dbms_output.put_line('前100个自然数中偶数之和是:'||sum_i);
end;
/

四、 PL/SQL游标

1、显式游标

显示游标是由用户声明和操作的一种游标,通常用于操作查询结果集(即由SELECT语句返回的查询结果),使用它处理数据的步骤包括声明游标、打开游标、读取游标和关闭游标4个步骤。

(1) 声明游标

声明游标主要包括游标名称和为游标提供结果集的SELECT语句。因此声明游标时,必须指定游标名称和游标所使用的人SELECT语句,声明游标格式如下:

CURSOR cur_name[input_parameter1 [,input_parameter2]...]
[RETURN ret_type]
IS select_sentence;

cur_name:表示所声明的游标名称;

ret_type:表示执行游标操作后的返回值类型,这是一个可选项;

select_srntence:游标所使用的SELECT语句,它为游标的反复读取提供了结果集;

input_parameter1:作为游标的输入参数,可以有多个,这是一个可选项。它指定用户在打开游标后向游标中传递的值,该参数的定义和初始化格式如下:

para_name [IN] DATATYPE [{:= | DEFAULT}para_value]

其中,para_name表示参数名称,其后面的关键字IN表示输入方向,可以省略;DATATYPE表示参数的数据类型,但数据类型不可以指定长度;para_value表示该参数的初始值或默认值,它也可以时一个表达式;para_name参数的初始值既可以以常规的方式赋值(:=),也可以使用关键字DEFAUKT初始化默认值。

示例:声明一个游标,用来读取emp表中职务为销售员的雇员信息:

declare
  /*声明游标,检索雇员信息*/  
  cursor cur_emp (var_job in varchar2:='SALESMAN')
  is select empno,ename,sal
    from emp
    where job=var_job;

(2)打开游标

在声明游标完毕之后,必须打开才能使用,打开游标的语法格式如下:

OPEN cur_name[(para_value1[,para_value2]...)]

cur_name:要打开的游标名称;

oara_value1:指定输入参数的值,根据声明游标时的实际情况,可以时多个或一个,这是一个可选项。如果在声明游标时定义了输入参数,并初始化其值,而在此处省略“输入参数”的值,则表示游标将使用“输入参数”的初始值;若在此处指定“输入参数”的值,则表示游标将使用这个指定的参数值。

打开游标就是执行定义的SELECT语句。执行完毕,查询结果装入内存,游标停在查询结果的首部,注意并不是下一行。当打开一个游标时,会完成以下几件事。

检查连编变量的取值;

根据联编变量的取值,确定活动集;

活动集的指针指向第一行。

紧接上一个例子的代码,打开游标的代码如下:

open cur_emp('MANAGER');

上边这表语句表示打开游标’cur_emp‘,然后给游标的’输入参数‘赋值为“MANAGER”。当然这里可以省略“('MANAGER')”,这样表示“输入参数”的值仍然使用其初始值(即SALESMAN).

(3)读取游标

当打开一个游标之后,就可以读取游标中的数据了,读取游标就是逐行将结果集中的数据保存到变量中。读取游标使用FETCH...INTO语句,其语法格式:

FETCH cur_name IN {variable};

cur_name:要读取的游标的名称;

variable:一个变量列表或“记录”变量(RECORD类型),Oracle使用记录变量来存储游标中的数据,要比使用变量列表方便得多。

示例:声明一个检索emp表中雇员信息的游标,然后打开游标,并指定检索职务时MANAGER的雇员信息,接着使用FETCH...INTO语句和WHILE循环语句读取游标中的所有雇员信息,最后输出读取的雇员信息。

set serveroutput on
declare
  /*声明游标,检索雇员信息*/  
  cursor cur_emp (var_job in varchar2:='SALESMAN')
  is select empno,ename,sal
    from emp
    where job=var_job;  
  type record_emp is record --声明一个记录类型(RECORD类型)
  (
    /*定义当前记录的成员变量*/
    var_empno emp.empno%type,
    var_ename emp.ename%type,
    var_sal emp.sal%type
  );
  emp_row record_emp;--声明一个record_emp类型的变量
begin
  open cur_emp('MANAGER');
  fetch cur_emp into emp_row;--先让指针指向结果集中的第一行
  while cur_emp%found loop
    dbms_output.put_line(emp_row.var_ename||'的编号是'||emp_row.var_empno||',工资是'||emp_row.var_sal);
    fetch cur_emp into emp_row;--让指针指向结果集中的下一行
  end loop;
  close cur_emp;
end;
/

(4)  关闭游标

当所有的活动集都被检索以后,游标就应该被关闭。PL/SQL程序将告知对于游标的处理已经结束,与游标相关联的资源可以被释放了。这些资源包括用来存储活动集的存储空间,以及用来存储活动集的临时空间。关闭游标的语法格式如下:

CLOSE cur_name;

参数cur_name表示要关闭的游标名称。一旦关闭了游标,也就关闭了SELECT操作,释放了占用的内存区。如果再从游标提取数据就是非法的,这样做会产生下面的Oracle错误。

ORA-1001:lnvalid CUSOR   ---非法游标

ORA-1002:FETCH out of sequence --超出界限

类似的,关闭一个已经被关闭的游标也是非法的,这也会触发ORA-1001错误。

例如:上个例子中,在读取完结果集之后,使用如下的CLOSE语句关闭游标

SQL> close cur_emp;---关闭游标

2、隐式游标

在执行一个SQL语句时,Oracle会自动创建一个隐式游标。这个游标时内存中处理该语句的工作区域。隐式游标主要时处理数据操作语句(如UPDATE、DELETE语句)的执行结果,当然特殊情况下,也可以处理SELECT语句的查询结果。由于饮食游标也有属性,当使用隐式游标的属性时,需要在属性前面加上隐式游标的默认名称——SQL。

3、游标的属性

无论时显式游标还是隐式游标,都具有%FOUND、%NOTFOUND、%ROWCOUNT和%ISOPEN4个属性,通过这4个属性可以获知SQL语句的执行结果以及该游标的状态信息。它们描述与游标操作相关的DML语句的执行情况。游标属性只能用在PL/SQL的流程控制语句内,而不能用在SQL语句内。下面对4个游标属性进行讲解:

(1)是否找到游标%FOUND

该属性表示当前游标是否指向有效一行,若是则值为TRUE,否则值为FALSE。检查此属性可以判断是否结束游标使用;

(2)是否没找到游标%NOTDOUND

该属性与%FOUND属性相类似,但其值恰好相反。

(3)游标行数%ROWCOUNT

该属性记录了游标抽取过的记录行数,也可以理解为当前游标所在的行号。这个属性在循环判断中也很有用,使得不必抽取所有记录行就可以终端游标操作;

(4)游标是否打开%ISOPEN

该属性表示游标是否处于打开状态。在实际应用中,使用一个游标前第一步往往是检查它得%ISOPEN属性,看其是否已打开,若没有,要打开游标在向下操作。这是防止运行过程中出错得必备一步。

4、游标变量

(1)声明游标变量

游标变量是一种引用类型。当程序运行时,他们可以指向不同得寻存储单元。如果要使用引用类型,首先要声明变量,然后相应得存储单元必须被分配。PL/SQL中得引用类型通过下述的而语法进行声明。

REF type

其中type是已经被定义的类型。REF关键字指明新的类型必须是一个指向经过定义的类型的指针。因此,游标可以使用的类型就是REF CURSOR.

定义一个有游标变量类型的完整语句如下:

TYPE<类型名> IS REF CURSOR

RETURN<返回类型>

其中,<类型名>是新的引用类型的名字,而<返回类型>是一个记录类型,它指明了最终由游标变量返回的选择列表的类型。

(2)打开游标变量

如果要将一个游标变量与特定的SELECT语句相关联,需要使用OPEN FOR语句,其语法如下:

OPEN<游标变量>FOR<SELECT>语句;

如果游标变量是受限的,则SELECT语句的返回类型必须与游标所限的记录类型匹配,如果不匹配,Oralce会返回错误ORA-6504。
(3)关闭游标变量

游标变量的关闭与静态游标的关闭类似,都是使用CLOSE语句。

5、通过FOR语句循环游标

在使用隐式游标或显式游标处理具有多行的结果集时,用户可以配合使用FOR语句来完成。在使用FOR语句遍历游标中的数据时,可以把它的计时器看作是一个自动的RECORD类型的变量。

(1)在FOR 语句中遍历隐式游标中的数据时,通常在关键字IN的后面提供由SELECT语句检索的结果集,在检索结果集的过程中,Oracle系统会自动提供一个隐式的游标SQL;

(2)在FOR语句中遍历显式游标中的数据时,通常在关键字IN的后面提供游标的名称,其语法格式如下:

FOR var_auto_record IN cur_name LOOP
plsqlsentence;
END LOOP;

var_auto_record:自动的RECORD类型的变量,可以是任意合法的变量名称;

cur_name:指定的游标名称;

plsqlsentence:PL/SQL语句;