我有一个游标循环,它通过将表的内容连接在一起构建一个字符串,使用以下代码的代码:
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;
但这似乎没有任何回报。
我应该使用哪种语法,以便每行只出现在结果字符串中一次?
答案 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)
简单回答,虽然可能不是最好的:
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;
答案 4 :(得分: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)都促进了这种退出循环解决方案,这显然是错误的!