For循环不打印游标中的最后一条记录

Posted

技术标签:

【中文标题】For循环不打印游标中的最后一条记录【英文标题】:For loop not printing last record in a cursor 【发布时间】:2021-06-30 08:41:22 【问题描述】:

所以,我正在使用游标,游标最初使用初始 for 循环运行,现在如果游标中的记录数超过一个,我需要执行操作,所以我首先获取记录数并存储在一个变量中并使用基于此的if条件。现在问题是当我运行整个过程时,该过程完成了它的工作,但只针对游标中的第一条记录并完全跳过第二条记录。请建议或帮助我找出错误。 添加代码sn-p。

for m in get_m_p(a,b)--主光标


循环 将get_m_p取入c_m;

g_m_p%notfound 时退出;

结束循环;

临时计数:= g_m_p%ROWCOUNT:


声明---

if(tempcount>1) 那么

声明----

如果结束;

结束循环;

对于两条记录,主光标在第一行返回,只对第一条进行操作,第二条记录被完全跳过。

【问题讨论】:

【参考方案1】:

这是多余的一行:

fetch get_m_p into c_m;

您不会在游标 FOR 循环中显式获取,而是在每次循环迭代中隐式完成。删除该行。


如何获取游标返回的行数?幸运的是,您似乎并不关心它是否返回了多少行(确切地说)。您只想知道它是否返回超过 1 行。所以,从字面上算数;如果计数器超过 1,则退出循环。

SQL> DECLARE
  2     CURSOR get_m_p IS
  3        SELECT *
  4          FROM emp
  5         WHERE deptno = 10;
  6
  7     l_cnt  NUMBER := 0;
  8  BEGIN
  9     FOR m IN get_m_p
 10     LOOP
 11        l_cnt := l_cnt + 1;
 12        EXIT WHEN l_cnt > 1;
 13     END LOOP;
 14
 15     DBMS_OUTPUT.put_line ('Cursor returned at least ' || l_cnt || ' row(s)');
 16
 17     IF l_cnt > 1
 18     THEN
 19        NULL;
 20     -- the rest of statements go here
 21     END IF;
 22  END;
 23  /
Cursor returned at least 2 row(s)

PL/SQL procedure successfully completed.

SQL>

由于无法知道游标将返回多少行,很遗憾,您必须先检查一下,然后再决定如何处理结果。

DECLARE
   CURSOR get_m_p IS
      SELECT *
        FROM emp
       WHERE deptno = 10;

   l_cnt  NUMBER := 0;
BEGIN
   SELECT COUNT (*)
     INTO l_cnt
     FROM (-- this is cursor's SELECT statement
           SELECT *
             FROM emp
            WHERE deptno = 10);

   FOR m IN get_m_p
   LOOP
      -- some statements here

      IF l_cnt > 1
      THEN
         NULL;
         -- statements to be executed if cursor return more than 1 row
      END IF;
   END LOOP;
END;
/

【讨论】:

好的,那我如何获得行数呢?最初我只使用了rowcount,没有使用整个loop-fetch-exit部分,但是它跳过了第一条记录的if条件。我了解到rowcount在获取之前第一次返回0,所以想这可能是问题并添加了那部分。 通过计数。我添加了更多信息,看看。 是的,看到了,明白了,但是if部分每次都需要执行,所以它需要在循环中,现在问题是如果我这样做,第一次计数仍然为 1,因此将跳过 if 部分,第二次开始这将起作用。这就是为什么我每次都试图获取游标中的总行数而不是通过迭代。所以每次都会检查条件,就好像我在为保证游标返回多于 1 行的特定情况执行此操作。因此,如果游标返回 2 行,则每行需要执行 if 部分。 但是同样的事情也将用于光标将返回一条记录的情况,因此 if 部分不需要执行,因此添加了特定条件。 我明白了;我再次编辑了答案,看看是否有帮助。【参考方案2】:

光标:

Oracle创建内存区域来处理SQL语句,称为上下文区域,游标是指向上下文区域的指针。游标保存 SQL 语句返回的行(一个或多个)。游标所持有的行集称为活动集。

有两种类型的光标

1. Implicit cursor
2. Explicit cursor 

隐式游标

每当执行 SQL 语句时,Oracle 都会自动创建隐式游标。任何 SQL 游标属性都将作为 sql%attribute_name 访问,如下面的示例所示。使用 SQL%ROWCOUNT 属性确定受影响的行数

DECLARE  
   no_of_records number(2); 
   BEGIN 
       select * from records; 
   IF sql%notfound THEN 
      dbms_output.put_line('no records present'); 
   ELSIF sql%found THEN 
      no_of_records := sql%rowcount;
      IF no_of_records > 1 THEN
          dbms_output.put_line('no of records ' || no_of_records); 
      END IF
   END IF;  
END; 

显式游标:

显式游标是程序员定义的游标,用于获得对上下文区域的更多控制。应在 PL/SQL 块的声明部分中定义显式游标。它是在返回多行的 SELECT 语句上创建的。

请看下面的例子:

DECLARE 
   r_id records.id%type; 
   
   CURSOR c_records is 
      SELECT id FROM records; 
BEGIN 
   OPEN c_records; 
   LOOP 
   FETCH c_records into r_id; 
      EXIT WHEN c_records%notfound; 
      dbms_output.put_line('Record id ' || r_id ); 
   END LOOP; 
   CLOSE c_records; 
END;

参考: https://www.tutorialspoint.com/plsql/plsql_cursors.htm

【讨论】:

【参考方案3】:

作为替代方案,您可以缓存每一行并在之后处理。 在 Oracle 11g Express Edition 上使用示例模式“HR”的示例:

DECLARE
  CURSOR get_m_p
  IS
    SELECT *
    FROM hr.employees
    WHERE department_id = 60
    order by employee_id;
  --
  rcEmp_last  get_m_p%rowtype;
  l_cnt       NUMBER;
BEGIN
  FOR rcM IN get_m_p LOOP
      l_cnt := get_m_p%rowcount;
      Dbms_Output.Put_Line('l_cnt='||l_cnt);
      if l_cnt=1 then
         rcEmp_last:=rcM;
      Else
         Dbms_Output.Put_Line('Process='||to_char(l_cnt-1));
         Dbms_Output.Put_Line('rcEmp_last.employee_id='||rcEmp_last.employee_id);
         --
         rcEmp_last:=rcM;
      END IF;
  End loop;
  --
  Dbms_Output.Put_Line('Exited FOR-LOOP');
  Dbms_Output.Put_Line('l_cnt='||l_cnt);
  --
  if l_cnt>1 then
     Dbms_Output.Put_Line('rcEmp_last.employee_id='||rcEmp_last.employee_id);
  End if;
END;

输出:

文字

PL/SQL block, executed in 1 ms
l_cnt=1
l_cnt=2
Process=1
rcEmp_last.employee_id=103
l_cnt=3
Process=2
rcEmp_last.employee_id=104
l_cnt=4
Process=3
rcEmp_last.employee_id=105
l_cnt=5
Process=4
rcEmp_last.employee_id=106
Exited FOR-LOOP
l_cnt=5
rcEmp_last.employee_id=107
Total execution time 35 ms

【讨论】:

以上是关于For循环不打印游标中的最后一条记录的主要内容,如果未能解决你的问题,请参考以下文章

SQL游标怎么循环更新

关于游标和for循环的问题

For循环不打印C++中的最后一个向量值

求一条sql循环语句

python中for循环前面换行最后不换行

在游标中调用函数 for 循环