如何声明一个弱类型SYS_REFCURSOR变量的%ROWTYPE?

时间:2012-06-25 10:16:22

标签: oracle stored-procedures plsql sys-refcursor rowtype

下面的Wrt代码我不能将fetch-into-variable的类型声明为基础表的%ROWTYPE,因为SYS_REFCURSOR位于连接两个表的select上,并且还选择了几个基于两个表的属性调用的函数;即我不能声明为L_RECORD T%ROWTYPE

---
DECLARE
  P_RS SYS_REFCURSOR;
  L_RECORD P_RS%ROWTYPE;
BEGIN
  CAPITALEXTRACT(
    P_RS => P_RS
  );
    OPEN P_RS;
    LOOP
      BEGIN
        FETCH P_RS INTO L_RECORD;
        EXIT WHEN P_RS%NOTFOUND;
        ...
      EXCEPTION
        WHEN OTHERS THEN
        ...
      END;
    END LOOP;
    CLOSE P_RS;
END;
--------
CREATE or REPLACE PROCEDURE CAPITALEXTRACT
(
    p_rs OUT SYS_REFCURSOR
) AS
BEGIN
  OPEN p_rs for 
     select t.*,tminusone.*, f(t.cash), g(t.cash) FROM T t, TMINUSONE tminusone
    where t.ticket=tminusone.ticket;
END CAPITALEXTRACT;

当然我不想在SYS_REFCURSOR中返回带有列的静态表R,然后声明为L_RECORD R%ROWTYPE。

因此问题: 如何声明一个弱类型SYS_REFCURSOR变量的%ROWTYPE?

1 个答案:

答案 0 :(得分:14)

简短的回答是,你做不到。您需要为每个要返回的列定义一个变量。

DECLARE
    P_RS SYS_REFCURSOR;
    L_T_COL1 T.COL1%TYPE;
    L_T_COL1 T.COL2%TYPE;
    ...

然后获取列列表:

FETCH P_RS INTO L_T_COL1, L_T_COL2, ... ;

只要你知道你在ref游标中的期望,这是痛苦但可管理的。在您的过程中使用T.*会使这个变得脆弱,因为向表中添加一列会破坏认为它知道哪些列以及它们处于什么顺序的代码。(如果在以下情况下,您也可以在环境之间打破它。这些表并不是一致的 - 我已经看到了不同环境中列排序不同的地方。您可能希望确保您只选择您真正关心的列,以避免为您永远不会阅读的内容定义变量。

从11g开始,您可以使用DBMS_SQL包将sys_refcursor转换为DBMS_SQL光标,然后您可以查询它以确定列。作为您可以执行的操作的示例,这将使用列名打印出每行中每列的值:

DECLARE
    P_RS SYS_REFCURSOR;
    L_COLS NUMBER;
    L_DESC DBMS_SQL.DESC_TAB;
    L_CURS INTEGER;
    L_VARCHAR VARCHAR2(4000);
BEGIN
    CAPITALEXTRACT(P_RS => P_RS);
    L_CURS := DBMS_SQL.TO_CURSOR_NUMBER(P_RS);
    DBMS_SQL.DESCRIBE_COLUMNS(C => L_CURS, COL_CNT => L_COLS,
        DESC_T => L_DESC);

    FOR i IN 1..L_COLS LOOP
        DBMS_SQL.DEFINE_COLUMN(L_CURS, i, L_VARCHAR, 4000);
    END LOOP;

    WHILE DBMS_SQL.FETCH_ROWS(L_CURS) > 0 LOOP
        FOR i IN 1..L_COLS LOOP
            DBMS_SQL.COLUMN_VALUE(L_CURS, i, L_VARCHAR);
            DBMS_OUTPUT.PUT_LINE('Row ' || DBMS_SQL.LAST_ROW_COUNT
                || ': ' || l_desc(i).col_name
                || ' = ' || L_VARCHAR);
        END LOOP;
    END LOOP;

    DBMS_SQL.CLOSE_CURSOR(L_CURS);
END;
/

这没有多少实际用途,为简洁起见,我将每个值都视为一个字符串,因为我只想打印它。查看文档并搜索更实际应用的示例。

如果你只需要你的引用光标中的几列,我想,你可以循环l_desc并记录column_name是你感兴趣的任何位置,作为数字变量;然后,您可以稍后通过该变量引用该列,您通常会在游标循环中使用该名称。取决于您对数据的处理方式。

但除非你期望不知道你要回来的列顺序,这是不可能的,因为你似乎控制了程序 - 并假设你摆脱了{{1} s - 你可能会更好地将返回的列减少到你需要的最小值,并且只是单独声明它们。