循环是指在程序中重复执行一条或多条语句、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_id fromlocations where country_id = 'CA';

 
LOCATION_ID CITY                    CO
----------- ------------------------------ 
       1800 Toronto                 CA
       1900 Whitehorse              CA
-- 目前有两条记录


 

SQL>edit 

DECLARE
        v_countryid     locations.country_id%TYPE :='CA';
        v_loc_id        locations.location_id%TYPE;
        v_counter       NUMBER(2) := 1;
        v_new_city      locations.city%TYPE := 'Montreal';
BEGIN
        SELECT MAX(location_id)
        INTO v_loc_id
        FROM locations
        WHERE country_id =v_countryid;
 
        LOOP
            INSERT INTOlocations(location_id, city, country_id)
            VALUES((v_loc_id +v_counter), v_new_city, v_countryid);
            v_counter := v_counter+ 1;
            EXIT WHEN v_counter> 3;
           -- EXIT后面可以加上标签,来指定退出哪一层循环
        END LOOP;
 
        COMMIT;
END;
/

 

SQL>/ 

PL/SQL procedure successfully completed.

 

SQL>select location_id, city, country_id from locations where country_id = 'CA'; 

LOCATION_ID CITY                           CO
----------- ------------------------------ --
       1800 Toronto                        CA
       1900 Whitehorse                     CA
       1901 Montreal                       CA
       1902 Montreal                       CA
       1903 Montreal                       CA            
-- 执行了循环语句后,多了三条记录


 

                

二、WHILE循环

         WHILE循环的格式如下:

 

              WHILE condition LOOP

                              statement1

                              statement2

                              ...

              END LOOP

 

         WHILE循环是指当condition的结果为TRUE时,就执行循环体。它的循环体可以执行0次或多次,比较适用于循环次数不确定的情况。当条件不满足,就会自动退出循环。

         现在使用WHILE循环来改写之前的例子:

 

SQL>edit 

 DECLARE
        v_countryid     locations.country_id%TYPE :='CA';
        v_loc_id        locations.location_id%TYPE;
        v_counter       NUMBER(2) := 1;
        v_new_city      locations.city%TYPE := 'Montreal';
BEGIN
        SELECT MAX(location_id)
        INTO v_loc_id
        FROM locations
        WHERE country_id =v_countryid;
 
        WHILE v_counter <= 3
              -- 使用WHILE,当v_counter小于等于3时就执行下面的语句
        LOOP
            INSERT INTOlocations(location_id, city, country_id)
            VALUES((v_loc_id +v_counter), v_new_city, v_countryid);
            v_counter := v_counter+ 1;
        END LOOP;
              -- 没有循环出口EXIT,当v_counter大于3时会自动退出循环
        COMMIT;
END;
/

                               

SQL>/  

PL/SQL procedure successfully completed.

 

SQL>select location_id, city, country_id from locations where country_id = 'CA';

 

LOCATION_ID CITY                          CO

----------- ------------------------------ --

       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是关键字,表示反过来循环,如从101进行循环

               lower_bound..upper_bound LOOP

               --分别是下边界和上边界,变量counter和上下边界都必须是数值型

              statement1

              statement2

              ...

ENDLOOP;

 

          FOR循环多用于循环次数已知的情况,FOR循环不需要专门声明一个变量作为计数器。它的计数器出了FOR循环之后是不可以使用的,如果一定要使用,建议还是专门声明一个计数器。和其他语言的FOR循环不同的是,用户不能自定义步长,如果想自定义步长,可以用基本循环或WHILE循环代替。

         下面用FOR循环来改写之前的例子:

 

SQL>edit

DECLARE
        v_countryid     locations.country_id%TYPE :='CA';
        v_loc_id        locations.location_id%TYPE;
        v_new_city      locations.city%TYPE := 'Montreal';
BEGIN
        SELECT MAX(location_id)
        INTO v_loc_id
        FROM locations
        WHERE country_id =v_countryid;
 
        FOR i IN 1..3
          -- 这里的i不需要在DECLARE部分专门声明,1和3分别是这个循环的下边界和上边界
          --FOR循环的步长是固定的,不能自行定义
        LOOP
            INSERT INTOlocations(location_id, city, country_id)
            VALUES((v_loc_id + i),v_new_city, v_countryid);
        END LOOP;
      --  DBMS_OUTPUT.PUT_LINE('The counter is ' || i);
          -- 第一次执行先打开注解,用来验证计数器出了循环之后的情况。
        COMMIT;
END;
/

 

SQL>/

*
ERROR at line 16:
ORA-06550: line 16, column 44:
PLS-00201: identifier 'I' must be declared
ORA-06550: line 16, column 2:
PL/SQL: Statement ignored
-- 报错是因为计数器i没有声明,因此出了FOR循环就不能使用了。


 

SQL>/

PL/SQL procedure successfully completed.

 

SQL>select location_id, city, country_id from locations where country_id = 'CA';

 

LOCATION_ID CITY                           CO
----------- ------------------------------ --
       1800 Toronto                        CA
       1900 Whitehorse                     CA
       1901 Montreal                       CA
       1902 Montreal                       CA
       1903 Montreal                       CA
       1904 Montreal                       CA
       1905 Montreal                       CA
       1906 Montreal                       CA
       1907 Montreal                       CA
       1908 Montreal                       CA
       1909 Montreal                       CA
 
11 rows selected.
-- 又多了三条记录


 

      FOR循环是比较受欢迎一种循环,因为它的循环次数可控,不过使用FOR循环,需要注意一些基本的规则:

 

A. 计数器counter只能在循环内部引用,它不需要在循环外部定义;

             

B. 不要为计数器counter赋值,它的赋值是自动完成的,但是可以将计数器的值赋值给其他变量;

             

C. 不要使用NULL值作为循环次数的下界和上界;

             

D. 下界和上界可以为非整数,但非整数会被自动计算为整数;

             

E. 加了关键字REVERSE之后表示循环的次数为倒序,即从上界向下界执行,但是即使使用了REVERSE,下界的值仍然应该比上界的值小,下面举个例子来演示REVERSE的用法:

             

          SQL> edit

                             

    BEGIN
         DBMS_OUTPUT.PUT_LINE('--------Normal--------');
         FORi IN 1..3
         -- 正常顺序
         LOOP
             DBMS_OUTPUT.PUT_LINE('Outputis ' || i);
         ENDLOOP;
               
         DBMS_OUTPUT.PUT_LINE('------Reverse-------');
         FORi IN REVERSE 1..3
         -- 加了关键字REVERSE会逆序执行
         LOOP
             DBMS_OUTPUT.PUT_LINE('Outputis ' || i);
         END LOOP;
               
         DBMS_OUTPUT.PUT_LINE('-----Upperand Lower------');
         FORi IN REVERSE 3..1
         -- 虽然使用了REVERSE关键字,但是下边界比上边界大,会出现不可控的现象
         LOOP
             DBMS_OUTPUT.PUT_LINE('Outputis ' || i);
         END LOOP;
     END;
     /

 

 SQL> /

             

--------Normal--------
Output is 1
Output is 2
Output is 3
------Reverse-------
Output is 3
Output is 2
Output is 1
-----Upper andLower------
--这一段没有执行,因为下界大于上界,会被认为结果为FALSE,故不进循环体。
               
PL/SQL proceduresuccessfully completed.

 

四、循环种类的选择

 

        循环种类的选择应根据实际需要,这里仅提供一些基本的建议:

1. 循环体必须至少执行一次,建议使用Basic LOOP

2. 先对条件进行判断,以决定循环体执行0次或多次,尤其是循环次数不确定的情况,建议使用WHILE循环;

3. 循环次数已知,建议使用FOR循环。

             

五、循环的嵌套

         循环可以嵌套,但注意嵌套的次数不要过多,最好不要超过3层;使用标签来区分代码块和循环体是个不错的编程习惯;EXIT结合标签使用,可以指定当前循环退出到哪一层循环,下面来看一个小例子:

wKioL1P-Pb_AqTmbAABqhPDnzWY817.png


六、CONTITUE关键字

        CONTITUEORACLE11g中引入的概念,如果数据库的版本是11g以前的,则不能用CONTINUE

CONTITUE的用法为:

              1. 跳过当前语句,但不退出循环,直接进入下一轮循环;

              2. 它的句法和EXIT一样,可以使用

                              a. CONTINUE

                                   -- 不附加条件

                              b.CONTINUE WHEN condition;

                                  --附加条件

                              c.CONTINUE label

                                  -- 跳转到某一个标签

                             

         11g以前如果要实现CONTINUE的功能,需要写更为复杂的程序,引入CONTINUE可以的简化程序,提高性能。下面来看一个CONTINUE的例子:

             

SQL>edit

             

DECLARE
        v_total SIMPLE_INTEGER :=0;
BEGIN
        FOR i IN 1..10 LOOP
            v_total := v_total + i;
           DBMS_OUTPUT.PUT_LINE('Total is: ' || v_total);
 
            CONTINUE WHEN i > 5;
              --当i大于5使,跳出当前这一轮循环,进入下一轮循环
            v_total := v_total + i;
           DBMS_OUTPUT.PUT_LINE('Out of Loop Total is' || v_total);
        END LOOP;
END;
/

               

SQL>/

Total is: 1
Out of Loop Total is2
Total is: 4
Out of Loop Total is6
Total is: 9
Out of Loop Total is12
Total is: 16
Out of Loop Total is20
Total is: 25
Out of Loop Total is30
-- i 大于5时跳出当前这一轮循环
Total is: 36
Total is: 43
Total is: 51
Total is: 60
Total is: 70
 
PL/SQL procedure successfully completed.

 

再来看一个CONTINUE和标签结合使用的例子:

 

SQL>edit

DECLARE
       v_total NUMBER := 0;
BEGIN
       <<BeforeTopLoop>>
      FOR i IN 1..10 LOOP
        v_total := v_total+ 1;
        DBMS_OUTPUT.PUT_LINE('---BeforeTopLoop---Total is: ' || v_total);
 
        FOR j IN 1..10 LOOP
          CONTINUE BeforeTopLoop WHEN i + j > 5;
          -- 跳转到标签BeforeTopLoop处
          v_total :=v_total + 1;
          DBMS_OUTPUT.PUT_LINE('---AfterTopLoop---Total is: ' || v_total);
        END LOOP;
      END LOOP;
END;

 

SQL>/

---BeforeTopLoop---Total is: 1
---AfterTopLoop---Total is: 2
---AfterTopLoop---Total is: 3
---AfterTopLoop---Total is: 4
---AfterTopLoop---Total is: 5
---BeforeTopLoop---Total is: 6
---AfterTopLoop---Total is: 7
---AfterTopLoop---Total is: 8
---AfterTopLoop---Total is: 9
---BeforeTopLoop---Total is: 10
---AfterTopLoop---Total is: 11
---AfterTopLoop---Total is: 12
---BeforeTopLoop---Total is: 13
---AfterTopLoop---Total is: 14
---BeforeTopLoop---Total is: 15
---BeforeTopLoop---Total is: 16
---BeforeTopLoop---Total is: 17
---BeforeTopLoop---Total is: 18
---BeforeTopLoop---Total is: 19
---BeforeTopLoop---Total is: 20
 
PL/SQL procedure successfully completed.

再来看一个使用CONTINUE从内层循环跳转到外层循环的例子:

 

SQL>edit

BEGIN
        <<outer>>
        FOR i IN 1..5
        LOOP
            DBMS_OUTPUT.PUT_LINE('Outer index = '|| TO_CHAR(i));
 
            <<inner>>
            FOR j IN 1..5
            
            LOOP
              DBMS_OUTPUT.PUT_LINE('--->Inner index = ' || TO_CHAR(j));
              -- 这条语句不会执行5次,因为每次执行后就会跳出这一层循环
              CONTINUE outer;
              
            END LOOP inner;
        END LOOP outer;
END;

 

SQL>/

Outer index = 1
--->Inner index = 1
Outer index = 2
--->Inner index = 1
Outer index = 3
--->Inner index = 1
Outer index = 4
--->Inner index = 1
Outer index = 5
--->Inner index = 1
 
PL/SQL procedure successfully completed.

             

六、GOTO 语句

          CONTINUE加上标签一起使用,和GOTO语句比较相似。GOTO语句可以无条件跳转到另一个代码块,但该代码块必须和跳转前的代码块处于同一可执行的section(如BEGINEXCEPTION部分)中。在实际编程中不建议使用GOTO,因为它不需要条件控制,容易破坏程序的可读性。

下面来看一个GOTO语句的例子:

 

SQL>edit         

BEGIN
    GOTO second_output;
              --直接跳转到标签所在的位置,下面这条语句永远都不是执行
    DBMS_OUTPUT.PUT_LINE('This linewill never execute.');
    <<second_output>>
    DBMS_OUTPUT.PUT_LINE('We arehere!');
END;
/

SQL>/

We are here!
PL/SQL procedure successfully completed.

 

GOTO语句使用时最好加上IF语句设定跳转的条件。