PL-SQL PROCEDURE问题

时间:2017-04-14 14:13:06

标签: oracle stored-procedures plsql

我正在使用Oracle DB和SQL Developer尝试这个简单的过程:

CREATE OR REPLACE PROCEDURE test_prod AS
   query_str VARCHAR2(200);
   i INTEGER := 0;

   TYPE cur IS REF CURSOR;
   my_cur cur;

BEGIN
   query_str := 'SELECT * FROM table WHERE ROWNUM<=1000';

   OPEN my_cur FOR query_str;

   DBMS_OUTPUT.PUT_LINE('start');

   LOOP
      i := i + 1;
      DBMS_OUTPUT.PUT_LINE('i: ' || i);
   END loop;

   DBMS_OUTPUT.PUT_LINE('COUNT: ' || i);

   CLOSE my_cur;
END;

&#39; i&#39;的最后一个值是3014(预期的一个是1000)和最后一个&#39; put_line&#39;没有显示。

此外,如果我尝试仅在循环中放入增量然后显示最终值,则该过程不会结束。

任何人都可以建议我问题出在哪里?

由于

3 个答案:

答案 0 :(得分:1)

我不想进入特定的游标参数(你可以在Internet / Oracle网站上找到很多教程)。只是为了向你展示你的存储过程的一些小编辑来制作你想要的东西(我不这样做这个提议的程序是最好的方法):

create or replace PROCEDURE TEST_PROD IS
   query_str VARCHAR2(200);
   i BINARY_INTEGER := 0;

   TYPE cur IS REF CURSOR;
   my_cur cur;

   TYPE rek IS TABLE OF table%ROWTYPE INDEX BY BINARY_INTEGER;
   my_rek rek;
BEGIN
   query_str := 'SELECT * FROM table WHERE ROWNUM<=1000';

   OPEN my_cur FOR query_str;

   DBMS_OUTPUT.PUT_LINE('start');

   LOOP
      i := i + 1;
      DBMS_OUTPUT.PUT_LINE('i: ' || i);
      FETCH my_cur INTO my_rek(i) ;
      EXIT WHEN my_cur%NOTFOUND;
   END loop;

   DBMS_OUTPUT.PUT_LINE('COUNT: ' || i);
   CLOSE my_cur;
END;

答案 1 :(得分:1)

Aleksej在可能的情况下使用隐式游标有一个很好的答案 - 隐式游标自动关闭并且简洁易读。对于您的示例,隐式游标是一种推荐的方法。

我将在这里添加一些与etsa的回答有关的其他示例 仅用于在原始帖子中使用显式游标的替代方法。

如果您想停止循环播放,则LOOP上需要退出条件。   明确您可以在计数器达到特定值时退出,或者当您的CURSOR已经退出时,或者您喜欢的任何其他条件时退出。

带有显式游标的示例1 - 当游标用完数据时退出循环:

创建一个测试表:

CREATE TABLE MY_TABLE(MY_TABLE_DATA NUMBER);

加载它:

INSERT INTO MY_TABLE SELECT ROWNUM FROM ALL_OBJECTS WHERE ROWNUM < 100;

然后创建您的程序:

CREATE OR REPLACE PROCEDURE TEST_PROD IS
  QUERY_STR VARCHAR2(200);
  I INTEGER := 0;
  TYPE CUR IS REF CURSOR;
  V_MY_TABLE_DATA MY_TABLE%ROWTYPE;
  MY_CUR CUR;
  BEGIN
    QUERY_STR := 'SELECT * FROM MY_TABLE WHERE ROWNUM<=5';
    OPEN MY_CUR FOR QUERY_STR;
    DBMS_OUTPUT.PUT_LINE('START');
    LOOP
      FETCH MY_CUR INTO V_MY_TABLE_DATA;
      EXIT WHEN MY_CUR%NOTFOUND;

      I := I + 1;
      DBMS_OUTPUT.PUT_LINE('I: ' || I);
       DBMS_OUTPUT.PUT_LINE('MY-TABLE-DATA: ' || V_MY_TABLE_DATA.MY_TABLE_DATA);
    END LOOP;
    DBMS_OUTPUT.PUT_LINE('COUNT: ' || I);
    CLOSE MY_CUR;
  END;
/

试一试:

BEGIN
  TEST_PROD();
END;
  /
START
I: 1
MY-TABLE-DATA: 1
I: 2
MY-TABLE-DATA: 2
I: 3
MY-TABLE-DATA: 3
I: 4
MY-TABLE-DATA: 4
I: 5
MY-TABLE-DATA: 5
COUNT: 5

或者您可以根据I停止循环:

CREATE OR REPLACE PROCEDURE TEST_PROD IS
  QUERY_STR VARCHAR2(200);
  I INTEGER := 0;
  TYPE CUR IS REF CURSOR;
  V_MY_TABLE_DATA MY_TABLE%ROWTYPE;
  MY_CUR CUR;
  BEGIN
    QUERY_STR := 'SELECT * FROM MY_TABLE WHERE ROWNUM<=10';
    OPEN MY_CUR FOR QUERY_STR;
    DBMS_OUTPUT.PUT_LINE('START');
    LOOP
      IF I >= 3 THEN
        EXIT;
      END IF;
      FETCH MY_CUR INTO V_MY_TABLE_DATA;
      I := I + 1;
      DBMS_OUTPUT.PUT_LINE('I: ' || I);
      DBMS_OUTPUT.PUT_LINE('MY-TABLE-DATA: ' || V_MY_TABLE_DATA.MY_TABLE_DATA);
    END LOOP;
    DBMS_OUTPUT.PUT_LINE('COUNT: ' || I);
    CLOSE MY_CUR;
  END;
/

BEGIN
  TEST_PROD();
END;
  /

START
I: 1
MY-TABLE-DATA: 661
I: 2
MY-TABLE-DATA: 662
I: 3
MY-TABLE-DATA: 663
COUNT: 3

答案 2 :(得分:0)

这里的主要问题是你的循环没有结束条件:你的循环与你打开的光标无关,所以它会永远循环,无论光标的行数是多少。

如果您需要循环选择查询的结果,这是一种简单的方法:

create or replace procedure testLoop is
begin
    for rec in ( 
                select level as val 
                from dual 
                connect by level <= 5 
               )
    loop
        dbms_output.put_line(rec.val);
    end loop;
end;

电话:

SQL> exec testLoop;
1
2
3
4
5