【问题标题】:Oracle CURSOR%ROWTYPE common for many cursors having only some columns the sameOracle CURSOR%ROWTYPE 常见于许多只有某些列相同的游标
【发布时间】:2013-08-02 18:22:22
【问题描述】:

您好,我有几个表ITEMS_*,它们可能有不同的列,但存在某些列并且它们的类型都相同。一个例子:

CREATE TABLE "APT"."ITEMS_AV" (
    -- These columns are common for all tables
    "ID" NUMBER NOT NULL ENABLE, 
    "CODE" VARCHAR2(20) NOT NULL ENABLE,
    "DESCRIPTION" VARCHAR2(50) NOT NULL ENABLE,
    -- Other columns may differ
    ...,
    -- The primary key is the same in all tables
    CONSTRAINT "ITEMS_AV_PK" PRIMARY KEY ("ID")
) TABLESPACE "APT" ;

对于每个表,我编写了一个特殊的 PL/SQL 过程来处理表中的数据。每个过程中都声明了一个游标'cur':

CREATE OR REPLACE
PROCEDURE PROCESS_ITEMS_AV AS
    CURSOR cur IS
        SELECT ID, CODE, DESCRIPTION, ...
        FROM ITEMS_AV;
BEGIN
    FOREACH d IN cur LOOP
        FIX_DATA(d);
        -- Continue processing 'row' data
        ...
    END LOOP
END PROCESS_ITEMS_AV;

在这个过程中,我需要调用过程 FIX_DATA 来修复当前行中的数据。例如从 row.CODE 中删除空格并从 row.DESCRIPTION 中删除无效字符。通常我会为每个单独的表声明这个过程:

CREATE OR REPLACE 
PROCEDURE FIX_DATA (r IN OUT ITEMS_AV%ROWTYPE) AS 
BEGIN
    r.CODE := ...;
    r.DESCRIPTION := ...;
END TESTPROC;

但是此过程对所有表都执行相同的操作,因此我希望只有一个过程 FIX_DATA 将在所有 PROCESS_ITEMS_* 过程中使用。问题是参数'r'声明中的表名:

PROCEDURE FIX_DATA (r IN OUT ITEMS_AV%ROWTYPE) AS ...

有没有办法声明 'r' 参数,以便我可以将它用于许多只有一些(但总是相同)列相同的游标?

提前非常感谢。 沃杰科技

【问题讨论】:

  • 我建议不要一次发送一行,而是将一列传递给您的修复数据。这需要在 Process_items_av 过程中再进行一次迭代
  • 我考虑过这样做,但如果我以后需要修复更多列,我必须更改 FIX_DATA 过程和所有 PROCESS_ITEMS_* 过程。因此,我宁愿传递一行,因为它只需要更改 FIX_DATA proc。

标签: oracle oracle11g


【解决方案1】:

您不能传递通用的rowtype。除非你想传递一个引用光标并通过dbms_sql 处理数据操作,否则我能想到的最接近的是创建一个对象类型并填充并传递它:

create type fix_data_obj is object(col1 varchar2(1),
  col2 number,
  col3 date,
  constructor function fix_data_obj return self as result);
/

create type body fix_data_obj is
  constructor function fix_data_obj return self as result is
  begin
    return;
  end;
end;
/

该对象定义了所有公共列,如果您需要处理更多列,您可以将它们添加到对象中。

create procedure fix_data (obj in out fix_data_obj) is
begin
  obj.col1 := 'Y';
  /* and other columns */
end fix_data;
/

fix_data 然后可以接受并更新对象;再次,如果添加更多列,它们只需要在这里处理。

create procedure process_dual is
  cursor c is select * from dual;
  tmp_obj fix_data_obj := fix_data_obj();
begin
  for r in c loop
    dbms_output.put_line('Original value: ' || r.dummy);
    tmp_obj.col1 := r.dummy;
    fix_data(tmp_obj);
    r.dummy := tmp_obj.col1;
    dbms_output.put_line('Fixed value: ' || r.dummy);
  end loop;
end process_dual;
/

这是最痛苦的部分。您需要从光标填充对象中的相关列,然后在调用fix_data 后从对象更新光标。但是,如果在对象中添加了新列,则不需要修改现有的处理过程,除了具有相关列的那些。这样至少可以最大限度地减少新列的影响。

exec process_dual;

anonymous block completed
Original value: X
Fixed value: Y

如果您将所有过程都放在一个包中,这会更简洁一些,那么您也可以在包中声明一个记录类型,而不是一个独立的对象类型。

将数据输入和输出对象/记录仍然是令人不快的部分。您可以有一个程序(或者更确切地说,程序,一种转换方式)来执行此操作,您可以在其中传递对象/记录和各个列;然后,如果添加了新列,则重载该过程,以免影响现有调用。

create procedure cols_to_obj(obj in out fix_data_obj, col1 in varchar2,
  col2 in number, col3 in date) is
begin
  obj.col1 := col1;
  obj.col2 := col2;
  obj.col3 := col3;
end cols_to_obj;
/

create procedure obj_to_cols(obj in fix_data_obj, col1 in out varchar2,
  col2 in out number, col3 in out date) is
begin
  col1 := obj.col1;
  col2 := obj.col2;
  col3 := obj.col3;
end obj_to_cols;
/

create or replace procedure process_dual is
  cursor c is select * from dual;
  tmp_obj fix_data_obj := fix_data_obj();
  null_number number;
  null_date date;
begin
  for r in c loop
    dbms_output.put_line('Original value: ' || r.dummy);
    cols_to_obj(tmp_obj, r.dummy, null, null);
    fix_data(tmp_obj);
    /* can't just pass null for missing fields */
    obj_to_cols(tmp_obj, r.dummy, null_number, null_date);
    dbms_output.put_line('Fixed value: ' || r.dummy);
  end loop;
end process_dual;
/

这样包装也会更整洁。或者,您可以将单个列直接传递给 fix_data,如果稍后添加新列,则重载 that

为每一列单独的fix_* 过程可能最终会更容易,除非您有依赖于多个列的值的修复。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-09-21
    • 2010-10-26
    • 2011-11-01
    • 2015-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-14
    相关资源
    最近更新 更多