9.PL_SQL——PL_SQL中的循环控制语句
循环是指在程序中重复执行一条或多条语句、PL/SQL中的循环主要有三种:
1.Basic Loop
2. FORLoop
3.WHILE Loop
下面来逐一介绍这三种循环的用法。
一、BasicLoops
基本循环的格式如下:
LOOP
statement1;
...
EXIT [WHENcondition];
END LOOP;
这里的EXIT是用来退出循环的,如果没有EXIT,则会变成死循环。
下面来看一个小例子:
SQL>select location_id, city, country_idfromlocations where country_id = 'CA';
LOCATION_IDCITYCO-----------------------------------------1800TorontoCA1900WhitehorseCA--目前有两条记录
SQL>edit
DECLAREv_countryidlocations.country_id%TYPE:='CA';v_loc_idlocations.location_id%TYPE;v_counterNUMBER(2):=1;v_new_citylocations.city%TYPE:='Montreal';BEGINSELECTMAX(location_id)INTOv_loc_idFROMlocationsWHEREcountry_id=v_countryid;LOOPINSERTINTOlocations(location_id,city,country_id)VALUES((v_loc_id+v_counter),v_new_city,v_countryid);v_counter:=v_counter+1;EXITWHENv_counter>3;--EXIT后面可以加上标签,来指定退出哪一层循环ENDLOOP;COMMIT;END;/
SQL>/
PL/SQLproceduresuccessfullycompleted.
SQL>select location_id, city, country_id from locations where country_id = 'CA';
LOCATION_IDCITYCO-------------------------------------------1800TorontoCA1900WhitehorseCA1901MontrealCA1902MontrealCA1903MontrealCA--执行了循环语句后,多了三条记录
二、WHILE循环
WHILE循环的格式如下:
WHILE condition LOOP
statement1;
statement2;
...
END LOOP;
WHILE循环是指当condition的结果为TRUE时,就执行循环体。它的循环体可以执行0次或多次,比较适用于循环次数不确定的情况。当条件不满足,就会自动退出循环。
现在使用WHILE循环来改写之前的例子:
SQL>edit
DECLAREv_countryidlocations.country_id%TYPE:='CA';v_loc_idlocations.location_id%TYPE;v_counterNUMBER(2):=1;v_new_citylocations.city%TYPE:='Montreal';BEGINSELECTMAX(location_id)INTOv_loc_idFROMlocationsWHEREcountry_id=v_countryid;WHILEv_counter<=3--使用WHILE,当v_counter小于等于3时就执行下面的语句LOOPINSERTINTOlocations(location_id,city,country_id)VALUES((v_loc_id+v_counter),v_new_city,v_countryid);v_counter:=v_counter+1;ENDLOOP;--没有循环出口EXIT,当v_counter大于3时会自动退出循环COMMIT;END;/
SQL>/
PL/SQLproceduresuccessfullycompleted.
SQL>select location_id, city, country_id from locations where country_id = 'CA';
LOCATION_ID CITYCO
----------- ------------------------------ --
1800 Toronto CA
1900 Whitehorse CA
1901 Montreal CA
1902 Montreal CA
1903 Montreal CA
1904 Montreal CA
1905 Montreal CA
1906 Montreal CA
8 rows selected.
-- 又多了三条记录
如果条件一次都不满足,WHILE循环会出现一次都不执行的情况。
三、FOR循环
FOR循环的基本格式为:
FORcounter IN [REVERSE]
-- REVERSE是关键字,表示反过来循环,如从10到1进行循环
lower_bound..upper_bound LOOP
--分别是下边界和上边界,变量counter和上下边界都必须是数值型
statement1;
statement2;
...
ENDLOOP;
FOR循环多用于循环次数已知的情况,FOR循环不需要专门声明一个变量作为计数器。它的计数器出了FOR循环之后是不可以使用的,如果一定要使用,建议还是专门声明一个计数器。和其他语言的FOR循环不同的是,用户不能自定义步长,如果想自定义步长,可以用基本循环或WHILE循环代替。
下面用FOR循环来改写之前的例子:
SQL>edit
DECLAREv_countryidlocations.country_id%TYPE:='CA';v_loc_idlocations.location_id%TYPE;v_new_citylocations.city%TYPE:='Montreal';BEGINSELECTMAX(location_id)INTOv_loc_idFROMlocationsWHEREcountry_id=v_countryid;FORiIN1..3--这里的i不需要在DECLARE部分专门声明,1和3分别是这个循环的下边界和上边界--FOR循环的步长是固定的,不能自行定义LOOPINSERTINTOlocations(location_id,city,country_id)VALUES((v_loc_id+i),v_new_city,v_countryid);ENDLOOP;--DBMS_OUTPUT.PUT_LINE('Thecounteris'||i);--第一次执行先打开注解,用来验证计数器出了循环之后的情况。COMMIT;END;/
SQL>/
*ERRORatline16:ORA-06550:line16,column44:PLS-00201:identifier'I'mustbedeclaredORA-06550:line16,column2:PL/SQL:Statementignored--报错是因为计数器i没有声明,因此出了FOR循环就不能使用了。
SQL>/
PL/SQLproceduresuccessfullycompleted.
SQL>select location_id, city, country_id from locations where country_id = 'CA';
LOCATION_IDCITYCO-------------------------------------------1800TorontoCA1900WhitehorseCA1901MontrealCA1902MontrealCA1903MontrealCA1904MontrealCA1905MontrealCA1906MontrealCA1907MontrealCA1908MontrealCA1909MontrealCA11rowsselected.--又多了三条记录
FOR循环是比较受欢迎一种循环,因为它的循环次数可控,不过使用FOR循环,需要注意一些基本的规则:
A. 计数器counter只能在循环内部引用,它不需要在循环外部定义;
B. 不要为计数器counter赋值,它的赋值是自动完成的,但是可以将计数器的值赋值给其他变量;
C. 不要使用NULL值作为循环次数的下界和上界;
D. 下界和上界可以为非整数,但非整数会被自动计算为整数;
E. 加了关键字REVERSE之后表示循环的次数为倒序,即从上界向下界执行,但是即使使用了REVERSE,下界的值仍然应该比上界的值小,下面举个例子来演示REVERSE的用法:
SQL> edit
BEGINDBMS_OUTPUT.PUT_LINE('--------Normal--------');FORiIN1..3--正常顺序LOOPDBMS_OUTPUT.PUT_LINE('Outputis'||i);ENDLOOP;DBMS_OUTPUT.PUT_LINE('------Reverse-------');FORiINREVERSE1..3--加了关键字REVERSE会逆序执行LOOPDBMS_OUTPUT.PUT_LINE('Outputis'||i);ENDLOOP;DBMS_OUTPUT.PUT_LINE('-----UpperandLower------');FORiINREVERSE3..1--虽然使用了REVERSE关键字,但是下边界比上边界大,会出现不可控的现象LOOPDBMS_OUTPUT.PUT_LINE('Outputis'||i);ENDLOOP;END;/
SQL> /
--------Normal--------Outputis1Outputis2Outputis3------Reverse-------Outputis3Outputis2Outputis1-----UpperandLower--------这一段没有执行,因为下界大于上界,会被认为结果为FALSE,故不进循环体。PL/SQLproceduresuccessfullycompleted.
四、循环种类的选择
循环种类的选择应根据实际需要,这里仅提供一些基本的建议:
1. 循环体必须至少执行一次,建议使用Basic LOOP;
2. 先对条件进行判断,以决定循环体执行0次或多次,尤其是循环次数不确定的情况,建议使用WHILE循环;
3. 循环次数已知,建议使用FOR循环。
五、循环的嵌套
循环可以嵌套,但注意嵌套的次数不要过多,最好不要超过3层;使用标签来区分代码块和循环体是个不错的编程习惯;EXIT结合标签使用,可以指定当前循环退出到哪一层循环,下面来看一个小例子:
六、CONTITUE关键字
CONTITUE是ORACLE11g中引入的概念,如果数据库的版本是11g以前的,则不能用CONTINUE。
CONTITUE的用法为:
1. 跳过当前语句,但不退出循环,直接进入下一轮循环;
2. 它的句法和EXIT一样,可以使用
a. CONTINUE;
-- 不附加条件
b.CONTINUE WHEN condition;
--附加条件
c.CONTINUE label;
-- 跳转到某一个标签
在11g以前如果要实现CONTINUE的功能,需要写更为复杂的程序,引入CONTINUE可以的简化程序,提高性能。下面来看一个CONTINUE的例子:
SQL>edit
DECLAREv_totalSIMPLE_INTEGER:=0;BEGINFORiIN1..10LOOPv_total:=v_total+i;DBMS_OUTPUT.PUT_LINE('Totalis:'||v_total);CONTINUEWHENi>5;--当i大于5使,跳出当前这一轮循环,进入下一轮循环v_total:=v_total+i;DBMS_OUTPUT.PUT_LINE('OutofLoopTotalis'||v_total);ENDLOOP;END;/
SQL>/
Totalis:1OutofLoopTotalis2Totalis:4OutofLoopTotalis6Totalis:9OutofLoopTotalis12Totalis:16OutofLoopTotalis20Totalis:25OutofLoopTotalis30--i大于5时跳出当前这一轮循环Totalis:36Totalis:43Totalis:51Totalis:60Totalis:70PL/SQLproceduresuccessfullycompleted.
再来看一个CONTINUE和标签结合使用的例子:
SQL>edit
DECLAREv_totalNUMBER:=0;BEGIN<<BeforeTopLoop>>FORiIN1..10LOOPv_total:=v_total+1;DBMS_OUTPUT.PUT_LINE('---BeforeTopLoop---Totalis:'||v_total);FORjIN1..10LOOPCONTINUEBeforeTopLoopWHENi+j>5;--跳转到标签BeforeTopLoop处v_total:=v_total+1;DBMS_OUTPUT.PUT_LINE('---AfterTopLoop---Totalis:'||v_total);ENDLOOP;ENDLOOP;END;
SQL>/
---BeforeTopLoop---Totalis:1---AfterTopLoop---Totalis:2---AfterTopLoop---Totalis:3---AfterTopLoop---Totalis:4---AfterTopLoop---Totalis:5---BeforeTopLoop---Totalis:6---AfterTopLoop---Totalis:7---AfterTopLoop---Totalis:8---AfterTopLoop---Totalis:9---BeforeTopLoop---Totalis:10---AfterTopLoop---Totalis:11---AfterTopLoop---Totalis:12---BeforeTopLoop---Totalis:13---AfterTopLoop---Totalis:14---BeforeTopLoop---Totalis:15---BeforeTopLoop---Totalis:16---BeforeTopLoop---Totalis:17---BeforeTopLoop---Totalis:18---BeforeTopLoop---Totalis:19---BeforeTopLoop---Totalis:20PL/SQLproceduresuccessfullycompleted.
再来看一个使用CONTINUE从内层循环跳转到外层循环的例子:
SQL>edit
BEGIN<<outer>>FORiIN1..5LOOPDBMS_OUTPUT.PUT_LINE('Outerindex='||TO_CHAR(i));<<inner>>FORjIN1..5LOOPDBMS_OUTPUT.PUT_LINE('--->Innerindex='||TO_CHAR(j));--这条语句不会执行5次,因为每次执行后就会跳出这一层循环CONTINUEouter;ENDLOOPinner;ENDLOOPouter;END;
SQL>/
Outerindex=1--->Innerindex=1Outerindex=2--->Innerindex=1Outerindex=3--->Innerindex=1Outerindex=4--->Innerindex=1Outerindex=5--->Innerindex=1PL/SQLproceduresuccessfullycompleted.
六、GOTO 语句
CONTINUE加上标签一起使用,和GOTO语句比较相似。GOTO语句可以无条件跳转到另一个代码块,但该代码块必须和跳转前的代码块处于同一可执行的section(如BEGIN或EXCEPTION部分)中。在实际编程中不建议使用GOTO,因为它不需要条件控制,容易破坏程序的可读性。
下面来看一个GOTO语句的例子:
SQL>edit
BEGINGOTOsecond_output;--直接跳转到标签所在的位置,下面这条语句永远都不是执行DBMS_OUTPUT.PUT_LINE('Thislinewillneverexecute.');<<second_output>>DBMS_OUTPUT.PUT_LINE('Wearehere!');END;/
SQL>/
Wearehere!PL/SQLproceduresuccessfullycompleted.
GOTO语句使用时最好加上IF语句设定跳转的条件。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。