【问题标题】:PL/SQL - Can you access certain record in a cursor by index?PL/SQL - 你可以通过索引访问游标中的某些记录吗?
【发布时间】:2017-10-10 11:48:15
【问题描述】:

我正在尝试编写一个测试,并且给定一组评论,我想将其中 1 个设置为“状态 A”,其余所有 设置为'状态 B'。我知道对于 PL/SQL,有一个用于 CURSOR 的 FOR LOOP 语法,我得到了它,所以它可以处理所有评论,但是有没有办法访问该游标中的特定记录?

我认为可以解决我的问题的是一种按索引访问光标的方法,类似于在其他语言中按索引访问数组的方式。 有没有办法在 PL/SQL 中做到这一点?

我对 PL/SQL 和游标的语法还是很陌生,所以我会用伪代码写出我想做的事情。

伪代码

CURSOR c_reviewer_ids IS
    SELECT id FROM reviewer_tbl
    WHERE course_id = 123;
v_first_id   reviewer_tbl.id%TYPE;

FOR i in 0..c_reviewer_ids.length
{
   IF i == 0
   {
      v_first_id := c_reviewer_ids(i);

      UPDATE reviewer_tbl
      SET status = 'Status A'
      WHERE id = c_reviewer_ids(i);
   }
   ELSE
   {
      UPDATE reviewer_tbl
      SET status = 'Status B'
      WHERE id = c_reviewer_ids(i);
   }
}

我能够制作一个 CURSOR FOR LOOP,但它处理每条记录的方式相同,我只想为一条记录做一些特别的事情。这是我目前拥有的:

CURSOR FOR LOOP(不处理特殊情况)

  CURSOR c_reviewer_ids IS
    SELECT id FROM reviewer_tbl
    WHERE course_id = 123;
  v_reviewer_id   reviewer_tbl.id%TYPE;

  FOR l_reviewer_id IN c_reviewer_ids 
  LOOP
      --Set the status for all reviewers.
      UPDATE reviewer_tbl
      SET status = 'Status B'
      WHERE reviewer_id = l_reviewer_id.id;

      --Save one of the ids; for this particular test, it doesn't matter if it is first or not
      v_reviewer_id := l_reviewer_id.id;
  END LOOP;

【问题讨论】:

  • 你为什么需要一个光标? case 构造不能解决问题吗?
  • @dan-bracuk,我不知道测试前的审稿人 ID,所以我无法将它们用作在case 语句中打开的条件。我只关心根据select 结果中的一条记录是否以不同方式处理来切换。对于我的具体情况,布尔标志就足够了......也许booleancase的组合会起作用......

标签: sql oracle for-loop plsql cursor


【解决方案1】:

您可以使用 BOOLEAN 来告诉您是否已经处理了光标的“第一”行,方式类似于以下:

DECLARE
  CURSOR c_reviewer_ids IS
    SELECT id FROM reviewer_tbl
      WHERE course_id = 123;
  v_reviewer_id   reviewer_tbl.id%TYPE;
  bFirst_row      BOOLEAN := TRUE;
BEGIN
  FOR l_reviewer_id IN c_reviewer_ids 
  LOOP
    IF bFirst_row THEN
      bFirst_row := FALSE;

      UPDATE reviewer_tbl
        SET status = 'Status A'
        WHERE id = l_reviewer_id.ID;
    ELSE
      --Set the status for all other reviewers.
      UPDATE reviewer_tbl
        SET status = 'Status B'
        WHERE reviewer_id = l_reviewer_id.ID;
    END IF;

    --Save one of the ids; for this particular test, it doesn't matter if it is first or not
    v_reviewer_id := l_reviewer_id.id;
  END LOOP;
END;

祝你好运。

编辑

如果您想访问第五行,您可以执行以下操作:

DECLARE
  v_reviewer_id   reviewer_tbl.id%TYPE;
BEGIN
  FOR l_reviewer_id IN (SELECT id, RN
                          FROM (SELECT ID, ROWNUM AS RN
                                  FROM reviewer_tbl
                                  WHERE course_id = 123)
                          WHERE RN = 5)
  LOOP
      UPDATE reviewer_tbl
        SET status = 'Status A'
        WHERE id = l_reviewer_id.ID;

    --Save one of the ids; for this particular test, it doesn't matter if it is first or not
    v_reviewer_id := l_reviewer_id.id;
  END LOOP;
END;

祝你好运。

【讨论】:

  • 谢谢 - 这能够解决我的具体问题。 :) 我仍然很好奇是否有更通用的解决方案......例如,如果我需要访问第 5 行怎么办?有没有办法访问索引5的记录?
  • 在提取五行之前没有第 5 行。完整的结果集在某些内部存储中不存在,等待您访问。如果有帮助,您可以使用 row_number() 分析函数将计算的行号添加到游标。
  • 查看我对答案的编辑。另请注意,在没有ORDER BY 子句的情况下,“第五行”的概念是任意的。祝你好运。
【解决方案2】:

您可以使用“BULK COLLECT INTO”子句将所有查询结果加载到实际内存数组中,然后使用基于索引的常规访问该数组。

这不是内存效率的(因此请注意,如果您正在处理大量记录,则不应这样做),但它确实有效:

看这个例子,我在内存数组中加载了“DICT”系统视图的前 100 条记录:

 declare
    type MEMTABLE_TYPE is 
        TABLE OF DICT%ROWTYPE index by binary_integer;

    myarray MEMTABLE_TYPE;
 begin
   select * 
   BULK COLLECT INTO myarray -- this loads the whole query result into the array
   from DICT
   where rownum < 100;

   -- scan all the array: 
   for c in 1..myarray.count loop
      dbms_output.put_line(myarray(c).table_name ||' -> ' || myarray(c).comments);
   end loop;             

   -- access directly the fifth element  
   dbms_output.put_line(myarray(5).table_name ||' -> ' || myarray(5).comments);
 end;   

无论如何,您不应该滥用这一点:除非您需要多次访问数据(因此将其保存在内存中而不是重新执行查询可以加快处理速度),否则您应该尝试使用常规游标。

【讨论】:

    【解决方案3】:

    使用this from the Oracle documentation,我找到了一种使用INDEX-BY 表的方法,该表与我在伪代码中想要的最接近。它创建游标,然后将其放入临时表中,然后可以对其进行索引。

    写完代码后,我意识到游标可能不是那么必要,因为它是立即关闭的。在这种情况下,可以省略光标,只使用 INDEX BY 表(类似于 Carlo 在他的回答中写的)。

    使用索引表 ALONG WITH 游标

    DEFINE
        CURSOR c_reviewer_ids IS
            SELECT id FROM reviewer_tbl
            WHERE course_id = 123;
    
        --table type
        TYPE reviewer_id_tbl_type IS TABLE OF c_reviewer_ids%ROWTYPE INDEX BY PLS_INTEGER;
        --actual table
        t_reviewer_ids reviewer_id_tbl_type;
    
        v_first_id   reviewer_tbl.id%TYPE;
    BEGIN
        --Fetch the cursor into the indexed table
        OPEN c_reviewer_ids;
        FETCH c_reviewer_ids BULK COLLECT INTO t_reviewer_ids;
        CLOSE c_reviewer_ids;  --cursor won't be used anymore
    
        --For loop
        FOR i IN 1..t_reviewer_ids.COUNT()  LOOP
           IF i = 1 THEN
              v_first_id := t_reviewer_ids(i).id;
    
              UPDATE reviewer_tbl
              SET status = 'Status A'
              WHERE id = t_reviewer_ids(i).id;
           ELSE
              UPDATE reviewer_tbl
              SET status = 'Status B'
              WHERE id = t_reviewer_ids(i).id;
           END IF;
        END LOOP;
    END;
    

    使用索引表代替游标

    DEFINE
        --table type
        TYPE reviewer_id_tbl_type IS TABLE OF reviewer_tbl.id%TYPE INDEX BY PLS_INTEGER;
        --actual table
        t_reviewer_ids reviewer_id_tbl_type;
    
        v_first_id   reviewer_tbl.id%TYPE;
    BEGIN
        --Fetch the query into the indexed table
        SELECT id
        BULK COLLECT INTO t_reviewer_ids
        FROM reviewer_tbl
        WHERE course_id = 123;
    
        --For loop; Notice that .id is not needed anymore
        FOR i IN 1..t_reviewer_ids.COUNT()  LOOP
           IF i = 1 THEN
              v_first_id := t_reviewer_ids(i); 
    
              UPDATE reviewer_tbl
              SET status = 'Status A'
              WHERE id = t_reviewer_ids(i);
           ELSE
              UPDATE reviewer_tbl
              SET status = 'Status B'
              WHERE id = t_reviewer_ids(i);
           END IF;
        END LOOP;
    END;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-05-07
      • 1970-01-01
      • 2021-08-20
      • 2011-01-18
      • 2011-02-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多