如何从游标中获取记录

Posted

技术标签:

【中文标题】如何从游标中获取记录【英文标题】:How to fetch the record from cursor 【发布时间】:2016-01-07 09:54:35 【问题描述】:

我正在尝试使用 PL SOL 游标从 Oracle 获取一些记录。这是我的代码,

程序参数:

PT_CLAIMS                IN      CLIM_BEAN_TAB,
PO_CLAIMS_CUSROR         OUT     SYS_REFCURSOR

迭代代码:

FOR I IN 1 .. PT_CLAIMS.LAST LOOP

        SELECT SEQ_TB_CLIAM.NEXTVAL INTO CLAIM_ID FROM DUAL;


        INSERT INTO TL_CLAIMS
        (
            CLAIM_ID,
            CLAIM_USER,
            CLAMIANT_ID
        )
        VALUES(
            CLAIM_ID,
            PT_CLAIMS(I).USER_ID,
            PT_CLAIMS(I).CLAMIANT_ID
        );

        OPEN PO_CLAIMS_CUSROR FOR
            SELECT CLAIM_ID AS CALIM_ID FROM DUAL; 


end loop;

我在 java 中使用 Out 游标。但是每当我使用 ResultSet 迭代出游标时,我只会得到一条记录。

如何将值添加到循环中的光标。

【问题讨论】:

【参考方案1】:

每次循环 IN 表内容时,您都会重新打开 OUT sys_refcursor。每次丢弃前一个 refcursor 并创建一个新的。 (希望 Oracle 在后台正确清理废弃的)。

当循环退出时,OUT 参数的声明 ID 对应于 IN 表中的最后一个条目。您不会将每个声明 ID 添加到单个 OUT refcursor - 您不能这样做。

您可以维护生成的 ID 的本地表,然后在循环之后从该本地表中打开 OUT 游标。或者,您可以在 forall 循环中用单个插入语句替换循环,以允许您使用 returning bulk collect into

create procedure p42(pt_claims in claim_bean_tab,
  po_claims_cursor out sys_refcursor)
as
  l_claim_ids sys.odcinumberlist;
begin
  forall i in 1..pt_claims.count
    insert into tl_claims(claim_id, claim_user_id, claimant_id)
    values (seq_tb_claim.nextval, pt_claims(i).user_id, pt_claims(i).claimant_id)
    returning claim_id bulk collect into l_claim_ids;

  open po_claims_cursor for
    select column_value from table(l_claim_ids);
end;
/

这使用了一个预定义的类型,它是一个数字的可变数组;因为它是一种 SQL 类型,所以它可以与table() 一起使用(作为table collection expression)。

SQL 小提琴似乎又被打破了,所以一个完整的工作示例:

create type claim_bean as object (user_id number, claimant_id number);
/

create type claim_bean_tab as table of claim_bean;
/

create table tl_claims (claim_id number, claim_user_id number, claimant_id number)
/

create sequence seq_tb_claim;
/

create procedure p42(pt_claims in claim_bean_tab,
  po_claims_cursor out sys_refcursor)
as
  l_claim_ids sys.odcinumberlist;
begin
  forall i in 1..pt_claims.count
    insert into tl_claims(claim_id, claim_user_id, claimant_id)
    values (seq_tb_claim.nextval, pt_claims(i).user_id, pt_claims(i).claimant_id)
    returning claim_id bulk collect into l_claim_ids;

  open po_claims_cursor for
    select column_value from table(l_claim_ids);
end;
/

set serveroutput on
declare
  l_claims claim_bean_tab;
  l_claims_cursor sys_refcursor;
  l_claim_id tl_claims.claim_id%type;
begin
  l_claims := new claim_bean_tab();
  l_claims.extend(3);
  l_claims(1) := new claim_bean(42, 123);
  l_claims(2) := new claim_bean(57, 456);
  l_claims(3) := new claim_bean(13, 789);

  p42(l_claims, l_claims_cursor);
  loop
    fetch l_claims_cursor into l_claim_id;
    exit when l_claims_cursor%notfound;
    dbms_output.put_line('Got claim ID from cursor: ' || l_claim_id);
  end loop;
  close l_claims_cursor;
end;
/

select * from tl_claims
/

PL/SQL procedure successfully completed.

Got claim ID from cursor: 1
Got claim ID from cursor: 2
Got claim ID from cursor: 3

【讨论】:

谢谢亚历克斯。然后如何在我的循环之前打开我的光标并在我的循环中插入值。 @sasikumar - 你不能修改游标,它是一个指向结果集的指针,而不是你可以添加或修改的东西。我添加了一个带有 returning 子句的批量插入示例,它将生成的序列 ID 放入本地表中,然后您可以将其用于引用光标。【参考方案2】:

这是因为您正在从变量中提取声明 ID,如果您想提取插入到表中的所有声明 ID,请使用 select claim_id from TL_CLAIMS。如果要提取表中存在的所有 CLAIM_ID,则必须在循环外打开光标。

FOR I IN 1 .. PT_CLAIMS.LAST LOOP

        SELECT SEQ_TB_CLIAM.NEXTVAL INTO CLAIM_ID FROM DUAL;


              INSERT INTO TL_CLAIMS
              (
                  CLAIM_ID,
                  CLAIM_USER,
                  CLAMIANT_ID
              )
              VALUES(
              CLAIM_ID,
              PT_CLAIMS(I).USER_ID,
              PT_CLAIMS(I).CLAMIANT_ID
              );

end loop;

OPEN PO_CLAIMS_CUSROR FOR
SELECT CLAIM_ID FROM TL_CLAIMS; 

更新: 或者,如果您想获取代码中生成的所有声明 ID 的列表,那么您可以编写类似这样的内容。

FOR I IN 1 .. PT_CLAIMS.LAST LOOP


        SELECT SEQ_TB_CLIAM.NEXTVAL INTO CLAIM_ID FROM DUAL;

            IF I = 1 THEN
                l_first_claim_id := CLAIM_ID;
            END IF;

              INSERT INTO TL_CLAIMS
              (
                  CLAIM_ID,
                  CLAIM_USER,
                  CLAMIANT_ID
              )
              VALUES(
              CLAIM_ID,
              PT_CLAIMS(I).USER_ID,
              PT_CLAIMS(I).CLAMIANT_ID
              );

end loop;

OPEN PO_CLAIMS_CUSROR FOR
    SELECT l_first_claim_id + ROWNUM - 1
      FROM dual
     CONNECT BY ROWNUM <= (CLAIM_ID - l_first_claim_id) + 1;

PS:这里的条件是没有来自另一个会话的同时呼叫。

【讨论】:

如果有两个同时调用,每个都必须得到一组连续的 ID 吗?【参考方案3】:

SELECT CLAIM_ID AS CALIM_ID FROM DUAL;总是返回一行,因为DUAL 只有一行。所以PO_CLAIMS_CUSROR 返回一行是预期的行为。

【讨论】:

以上是关于如何从游标中获取记录的主要内容,如果未能解决你的问题,请参考以下文章

基于游标的记录与强引用游标

如何传递从多个表创建的 PL/SQL 游标记录?

SQL Server中的游标是啥意思?

基于游标的记录与强引用游标

如何插入多条记录并获取标识值?

oracle的游标cursor