ORACLE PL/SQL 对于每个传递表名到过程
ORACLE PL/SQL for each passing tablename to procedure
我需要在过程中执行 FOR EACH 循环,但我需要动态传递 table 名称。
这是声明
CREATE OR REPLACE PROCEDURE MIGRATE_PRIMITIVES_PROPS
(
FromTable IN VARCHAR2,
ToTable IN VARCHAR2
)
当我尝试这样做时
FOR EachRow IN (SELECT * FROM FromTable)
它说 table 无效
进入过程的 table 是动态的,列一直在添加和删除,所以我无法拼出列并使用游标来填充它们。
您必须使用动态 SQL 来查询您在编译时不知道其名称的 table。您可以使用动态游标来做到这一点:
as
l_cursor sys_refcursor;
begin
open l_cursor for 'select * from ' || fromtable;
loop
fetch l_cursor into ...
...但随后它崩溃了,因为您也无法在该游标变量中动态定义要提取到 based on a weak ref cursor; and you don't know the column names or types you're actually interested in - you're using select *
and have specific names to exclude, not include. You mentioned an inner loop that works and gets the column names, but there is 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 中的列名,但仍然使用动态插入语句,因为 table 名称未知。 (这看起来很奇怪,但是......)
正在创建和填充示例 table,然后使用它们的名称调用过程:
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');
最终目标 table 包含:
select * from target_table;
KEY VALUE
------------------------------ ------------------------------
COL1 Test
COL2 42
COL3 2015-05-22 15:29:31
它是基本的,因为它不会清理输入(查找 the dbms_assert
package),并且不会对不同的数据类型进行任何特殊处理。在我的示例中,我的来源 table 有一个日期列;目标 table 根据调用会话的 NLS_DATE_FORMAT 设置获取该日期值的字符串表示形式,这并不理想。有一种简单但有点笨拙的方法来获得一致的日期格式,还有一种更好但更复杂的方法;但您可能没有日期值,所以这可能就足够了。
我需要在过程中执行 FOR EACH 循环,但我需要动态传递 table 名称。
这是声明
CREATE OR REPLACE PROCEDURE MIGRATE_PRIMITIVES_PROPS
(
FromTable IN VARCHAR2,
ToTable IN VARCHAR2
)
当我尝试这样做时
FOR EachRow IN (SELECT * FROM FromTable)
它说 table 无效
进入过程的 table 是动态的,列一直在添加和删除,所以我无法拼出列并使用游标来填充它们。
您必须使用动态 SQL 来查询您在编译时不知道其名称的 table。您可以使用动态游标来做到这一点:
as
l_cursor sys_refcursor;
begin
open l_cursor for 'select * from ' || fromtable;
loop
fetch l_cursor into ...
...但随后它崩溃了,因为您也无法在该游标变量中动态定义要提取到 based on a weak ref cursor; and you don't know the column names or types you're actually interested in - you're using select *
and have specific names to exclude, not include. You mentioned an inner loop that works and gets the column names, but there is 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 中的列名,但仍然使用动态插入语句,因为 table 名称未知。 (这看起来很奇怪,但是......)
正在创建和填充示例 table,然后使用它们的名称调用过程:
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');
最终目标 table 包含:
select * from target_table;
KEY VALUE
------------------------------ ------------------------------
COL1 Test
COL2 42
COL3 2015-05-22 15:29:31
它是基本的,因为它不会清理输入(查找 the dbms_assert
package),并且不会对不同的数据类型进行任何特殊处理。在我的示例中,我的来源 table 有一个日期列;目标 table 根据调用会话的 NLS_DATE_FORMAT 设置获取该日期值的字符串表示形式,这并不理想。有一种简单但有点笨拙的方法来获得一致的日期格式,还有一种更好但更复杂的方法;但您可能没有日期值,所以这可能就足够了。