【问题标题】:For loop not printing last record in a cursorFor循环不打印游标中的最后一条记录
【发布时间】: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) 那么

声明----

如果结束;

结束循环;

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

【问题讨论】:

    标签: for-loop plsql cursor rdbms


    【解决方案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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-07-28
        相关资源
        最近更新 更多