Oracle 游标在最后一项中运行两次
Posted
技术标签:
【中文标题】Oracle 游标在最后一项中运行两次【英文标题】:Oracle cursor running through the last item twice 【发布时间】:2009-05-22 04:14:16 【问题描述】:我有一个游标循环,它通过将表格的内容连接在一起来构建一个字符串,使用以下代码:
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;
但这似乎根本没有返回任何东西。
我应该使用什么样的语法,以便每一行只在结果字符串中出现一次?
【问题讨论】:
【参考方案1】:你可以试试这个:
OPEN cur_t;
LOOP
FETCH cur_t INTO v_texttoadd;
EXIT WHEN cur_t%notfound;
v_string := v_string || v_texttoadd;
END LOOP;
这是可行的,因为 %notfound 在执行 FETCH 时设置,并且没有更多行可获取。在您的示例中,您在连接后检查了 %notfound,结果,您最终得到了重复项。
【讨论】:
对我没有用。如果你在乎的话,看看我的回答。 美丽。就我而言,光标仍处于打开状态,并且不断迭代最后一项,永无止境。【参考方案2】:已经给出了正确的答案,只是稍微阐述一下。
模拟你目前的情况:
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.
但是,当使用 cursor-for-loop 时,您的 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 来完成这项工作。如果您使用的是 10g 或更高版本,则使用 SQL 模型子句的示例:
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.
问候, 抢。
【讨论】:
+1 FOR .. IN ... LOOP 建议和示例。光标 FOR 循环是您的朋友 ;-) 额外的好处:您通常可以在 FOR 循环中嵌入光标选择语句: FOR Rec IN (SELECT...) LOOP【参考方案3】:%notfound 在 fetch 未能检索到新行时设置。
另一种可能的方式(这个避免了“if”和“exit when”):
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;
【讨论】:
【参考方案4】:简单的答案,虽然可能不是最好的:
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;
【讨论】:
我认为,根据您的缩进,您假设 EXIT 语句是 LOOP 语法的一部分,并且必须出现在循环的末尾。这不是真的; EXIT 只是一个语句,就像循环体中的任何其他语句一样。这就是为什么正如其他人所展示的那样,您可以将其放在 FETCH 之后立即解决您的问题。 对我没有用。如果你在乎的话,看看我的回答。【参考方案5】:我在 Microsoft SQL 中制作了许多基于游标的解决方案,这些解决方案总是运行良好(过去的好日子!我非常希望我的 SQL Server 回来!)但是这个问题的所有答案对我来说都是错误的,最后一个游标的行总是执行两次,无论我多么严格地遵循他们建议的方法。
忘记非while 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 知识库文章和论坛帖子(以及 ***)都提倡这种明显错误的退出循环解决方案!
【讨论】:
以上是关于Oracle 游标在最后一项中运行两次的主要内容,如果未能解决你的问题,请参考以下文章