0

美好的一天,我有几个表 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'参数,以便我可以将它用于许多只有一些(但总是相同)列相同的游标?

提前谢谢了。沃杰科技

4

1 回答 1

3

你不能通过一个通用的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

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

于 2013-08-02T10:05:27.350 回答