每个将表名传递给过程的 ORACLE PL/SQL

Posted

技术标签:

【中文标题】每个将表名传递给过程的 ORACLE PL/SQL【英文标题】:ORACLE PL/SQL for each passing tablename to procedure 【发布时间】:2015-05-22 13:39:24 【问题描述】:

我需要在过程中执行 FOR EACH 循环,但我需要动态传递表名。

这是声明

CREATE OR REPLACE PROCEDURE MIGRATE_PRIMITIVES_PROPS
(
    FromTable IN VARCHAR2, 
    ToTable IN VARCHAR2
)

当我尝试这样做时 FOR EachRow IN (SELECT * FROM FromTable) 说表格无效

进入过程的表是动态的,列一直在添加和删除,所以我无法拼出列并使用游标填充它们。

【问题讨论】:

我有。一个代码 sn-p 肯定会有所帮助。 如果您是don't know their names at compile time,您将如何引用循环中的列(EachRow.col1 等)?你知道一些你感兴趣的静态的吗?还是结构完全是动态的?你对数据做了什么——希望不只是将其插入ToTable `FromTable 不是静态表名。如前所述,查找动态 sql。 我知道构成主键的列的名称。 ToTable 是一个键值对表。我知道这不是很好的做法,但是当权者都坚持这样做。所以我抓取了所有不在 FromTable 的 PK 中的列名,并将它们放入 ToTable。 还有第二个 FOR 循环,我正在执行 FOR EachColumn IN (SELECT COLUMN_NAME FROM ALL_TAB_COLUMNS WHERE TABLE_NAME = ''||FromTable||'' AND COLUMN_NAME != 'COL_NAME' AND COLUMN_NAME ! = 'LIB_NAME' AND COLUMN_NAME != 'PARTNAME' AND COLUMN_NAME != 'PRIMITIVE' AND COLUMN_NAME != 'PART_ROW') 有效,因为该查询中的表可以动态插入。我只需要帮助使第一个 FOR 循环正常工作: FOR EachRow IN (SELECT * FROM FromTable) 【参考方案1】:

您必须使用动态 SQL 来查询在编译时您不知道其名称的表。您可以使用动态光标来做到这一点:

as
    l_cursor sys_refcursor;
begin
    open l_cursor for 'select * from ' || fromtable;
    loop
      fetch l_cursor into ... 

...但随后它崩溃了,因为您无法定义要获取到based on a weak ref cursor 的记录类型;并且您不知道您真正感兴趣的列名或类型 - 您使用的是 select * 并且有特定的名称要排除,而不是包括在内。您提到了一个可以工作并获取列名的内部循环,但是该游标变量中也有动态的 no way to refer to a field。

所以你必须更加努力,使用the dbms_sql package而不是原生动态SQL。

这是一个基本版本:

create or replace procedure migrate_primitives_props
(
    fromtable in varchar2, 
    totable in varchar2
)
as
    l_cursor pls_integer;
    l_desc_tab dbms_sql.desc_tab;
    l_columns pls_integer;
    l_value varchar2(4000);
    l_status pls_integer;
begin
    l_cursor := dbms_sql.open_cursor;

    -- parse the query using the parameter table name
    dbms_sql.parse(l_cursor, 'select * from ' || fromtable, dbms_sql.native);
    dbms_sql.describe_columns(l_cursor, l_columns, l_desc_tab);

    -- define all of the columns
    for i in 1..l_columns loop
        dbms_sql.define_column(l_cursor, i, l_value, 4000);
    end loop;

    -- execute the cursor query
    l_status := dbms_sql.execute(l_cursor);

    -- loop over the rows in the result set
    while (dbms_sql.fetch_rows(l_cursor) > 0) loop
        -- loop over the columns in each row
        for i in 1..l_columns loop
            -- skip the columns you aren't interested in
            if l_desc_tab(i).col_name in ('COL_NAME', 'LIB_NAME', 'PARTNAME',
                'PRIMITIVE', 'PART_ROW')
            then
                continue;
            end if;

            -- get the column value for this row
            dbms_sql.column_value(l_cursor, i, l_value);
            -- insert the key-value pair for this row
            execute immediate 'insert into ' || totable
                || '(key, value) values (:key, :value)'
                using l_desc_tab(i).col_name, l_value;
        end loop;
    end loop;
end;
/

我假设您知道 ToTable 中的列名,但仍然使用动态插入语句,因为该表名是未知的。 (这看起来很奇怪,但是……)

创建和填充示例表,然后使用它们的名称调用过程:

create table source_table (col_name varchar2(30), lib_name varchar2(30),
  partname varchar2(30), primitive number, part_row number,
  col1 varchar2(10), col2 number, col3 date);
create table target_table (key varchar2(30), value varchar2(30));

insert into source_table (col_name, lib_name, partname, primitive, part_row,
  col1, col2, col3)
values ('A', 'B', 'C', 0, 1, 'Test', 42, sysdate);

exec migrate_primitives_props('source_table', 'target_table');

最终目标表包含:

select * from target_table;

KEY                            VALUE                        
------------------------------ ------------------------------
COL1                           Test                          
COL2                           42                            
COL3                           2015-05-22 15:29:31           

它是基本的,因为它没有清理输入(查找 the dbms_assert package),并且没有对不同的数据类型进行任何特殊处理。在我的示例中,我的源表有一个日期列;目标表根据调用会话的 NLS_DATE_FORMAT 设置获取该日期值的字符串表示,这并不理想。有一种简单但略显老套的方法来获得一致的日期格式,还有一种更好但更复杂的方法;但是您可能没有日期值,所以这可能已经足够了。

【讨论】:

谢谢。那真的很近。 Key-Value 表的主键也与 PRIMITIVES_PROPS 表相同,因此还有一些工作要做,但更接近。 @DanTheMan1966 - 如果您不知道列位置并且无法通过名称获取它,您总是可以在列上执行两个循环;第一个只查找命名列以将键放入变量中,然后第二个如上所述,插入包含在第一个循环中设置的键的行。 (我认为这是有道理的......)

以上是关于每个将表名传递给过程的 ORACLE PL/SQL的主要内容,如果未能解决你的问题,请参考以下文章

pl/sql 过程不允许将表名/视图名作为参数传递

将 oracle.sql.ARRAY 传递给 PL/SQL 过程时设置时区

在 PL/SQL 存储过程中将表作为参数传递

将oracle.sql.ARRAY传递给PL / SQL过程时设置时区

将表中的单个值分配给 ORACLE PL/SQL 中声明的变量时出错

oracle pl/sql 将异常类型传递给函数