Oracle游标两次运行最后一项

时间:2009-05-22 04:14:17

标签: oracle plsql

我有一个游标循环,它通过将表的内容连接在一起构建一个字符串,使用以下代码的代码:

OPEN cur_t;
LOOP
    FETCH cur_t INTO v_texttoadd;

    v_string := v_string || v_texttoadd;
EXIT WHEN cur_t%notfound;
END LOOP;

问题是,当然,最后一项被添加两次,因为系统再次运行它,然后才意识到找不到更多内容。

我试过玩

之类的东西
OPEN cur_t;
WHILE cur_t%found;
LOOP
    FETCH cur_t INTO v_texttoadd;

    v_string := v_string || v_texttoadd;
END LOOP;

但这似乎没有任何回报。

我应该使用哪种语法,以便每行只出现在结果字符串中一次?

5 个答案:

答案 0 :(得分:24)

你可以试试这个:

OPEN cur_t;
LOOP
  FETCH cur_t INTO v_texttoadd;
  EXIT WHEN cur_t%notfound;
  v_string := v_string || v_texttoadd;
END LOOP;

这是有效的,因为在执行FETCH时设置%notfound并且不再需要获取任何行。在您的示例中,您在连接后检查了%notfound,因此,您最终获得了副本。

答案 1 :(得分:3)

已经给出了正确的答案,但只是详细说明了一下。

模拟您当前的情况:

SQL> declare
  2    cursor cur_t
  3    is
  4    select ename
  5      from emp
  6     where deptno = 10
  7    ;
  8    v_texttoadd emp.ename%type;
  9    v_string    varchar2(100);
 10  begin
 11    open cur_t;
 12    loop
 13      fetch cur_t into v_texttoadd;
 14      v_string := v_string || v_texttoadd;
 15      exit when cur_t%notfound;
 16    end loop
 17    ;
 18    dbms_output.put_line(v_string);
 19  end;
 20  /
CLARKKINGMILLERMILLER

PL/SQL-procedure is geslaagd.

这里MILLER打印两次。只需切换EXIT语句和v_string赋值,就可以得到所需的结果:

SQL> declare
  2    cursor cur_t
  3    is
  4    select ename
  5      from emp
  6     where deptno = 10
  7    ;
  8    v_texttoadd emp.ename%type;
  9    v_string    varchar2(100);
 10  begin
 11    open cur_t;
 12    loop
 13      fetch cur_t into v_texttoadd;
 14      exit when cur_t%notfound;
 15      v_string := v_string || v_texttoadd;
 16    end loop
 17    ;
 18    dbms_output.put_line(v_string);
 19  end;
 20  /
CLARKKINGMILLER

PL/SQL-procedure is geslaagd.

但是,使用游标换环时,PL / SQL代码会变得更容易。然后,您可以跳过v_texttoadd变量,循环中的行数减少:

SQL> declare
  2    cursor cur_t
  3    is
  4    select ename
  5      from emp
  6     where deptno = 10
  7    ;
  8    v_string    varchar2(100);
  9  begin
 10    for r in cur_t
 11    loop
 12      v_string := v_string || r.ename;
 13    end loop
 14    ;
 15    dbms_output.put_line(v_string);
 16  end;
 17  /
CLARKKINGMILLER

PL/SQL-procedure is geslaagd.

您还可以使用直接SQL来完成工作。 SQL模型子句的示例,如果您使用的是10g或更高版本:

SQL> select string
  2    from ( select string
  3                , rn
  4             from emp
  5            where deptno = 10
  6            model
  7                  dimension by (rownum rn)
  8                  measures (ename, cast(null as varchar2(100)) string)
  9                  ( string[any] order by rn desc = ename[cv()] || string[cv()+1]
 10                  )
 11         )
 12   where rn = 1
 13  /

STRING
-----------------------------------------------------------------------------------
CLARKKINGMILLER

1 rij is geselecteerd.

此致 罗布。

答案 2 :(得分:2)

当获取无法检索新行时,

%notfound被设置。

另一种可能的方式(这个避开“if”和“退出时”):

OPEN cur_t;
FETCH cur_t INTO v_texttoadd;
WHILE cur_t%found LOOP
    v_string := v_string || v_texttoadd;
    FETCH cur_t INTO v_texttoadd;
END LOOP;

答案 3 :(得分:0)

我在Microsoft SQL中创建了许多基于游标的解决方案,这些解决方案总能完美运行(过去的好日子!我希望我的SQL服务器能够恢复这么多!)但是这个问题的所有答案对我来说都是错误的,最后一个无论我多么接近他们建议的方法,光标的行总是执行两次。

忘记非loop并忘记exit,这是你必须做的,以避免双重执行(几乎你在T-SQL中也做了!)。

  cursor c_mm is select a, b, c, d from mytable;

  begin
    open c_mm;
    fetch c_mm into a, b, c, d;
    while (not c_mm%notfound) loop 
        -- do what you have to do here

        fetch c_mm into a, b, c, d;
    end loop;
    close c_mm;
  end;

我真的不明白为什么所有Oracle知识库文章和论坛帖子(以及stackoverflow)都促进了这种退出循环解决方案,这显然是错误的!

答案 4 :(得分:0)

简单回答,虽然可能不是最好的:

OPEN cur_t;
LOOP
    FETCH cur_t INTO v_texttoadd;
    IF cur_t%found THEN
        v_string := v_string || v_texttoadd;
    END IF;
EXIT WHEN cur_t%notfound;
END LOOP;