我需要编写一个过程,它应该动态地返回任何给定表的插入脚本。以下是该程序的签名。 P_export_data_dml(table_name IN,column_name IN,column_value IN,data_dml OUT)
例: 表T1
C1 | C2
---------- | ----------
10 | 20
10 | 21
表T2
C1 | C2 | C3
------- | ------- | --------
10 | 20 | 30
10 | 21 | 31
case1
Input:
P_export_data_dml(T1, C1, 10, data_dml)
Expected output:
Insert into T1(C1, C2) values(10,20);
Insert into T1(C1, C2) values(10,21);
情形2
Input:
P_export_data_dml(T2, C1, 10, data_dml)
Expected output:
Insert into T2(C1, C2, C3) values(10, 20, 30);
Insert into T2(C1, C2, C3) values(10, 21, 31);
当表中只有一条记录用于给定输入时,我能够生成一个insert语句。 (使用all_tab_columns然后为每个列提取给定的表以形成Insert语句的一部分,然后最终连接所有字符串以形成最终的INSERT语句。)
但是当有多个记录时,我面临着形成插入语句的挑战。你能帮我解决一下这些记录的逻辑,并为所有记录形成INSERT语句。
答案 0 :(得分:1)
您的问题不清楚您计划用于data_dml OUT
的数据类型。
这可能是一个集合,或一个clob等。
请注意,大多数支持Oracle的编辑器,如SQL Developer,Intellij IDEA,TOAD等已经内置了这种东西,具有强大的实现来转换结果集进入INSERTS 。
话虽如此,使用Dynamic SQL可以实现这种功能。
以下是一些轻量级示例,这些示例建立在您计划仅使用NUMBER
用于column_value
参数的前提下。可以根据需要为其他人或ANYDATA
添加重载。
示例1:此示例将在INSERT
中返回多个CLOB
语句(可以使用集合返回类型)。它只会为INSERT
NUMBER
列生成DATA_TYPE
秒。注意这里没有错误处理。
CREATE OR REPLACE PROCEDURE P_EXPORT_DATA_DML(P_TABLE_NAME IN VARCHAR2, P_COLUMN_NAME IN VARCHAR2, P_COLUMN_VALUE IN NUMBER, V_DATA_DML OUT CLOB)
IS
P_COLUMN_LIST CLOB;
V_VALUES_LIST CLOB;
TYPE USER_TAB_COL_TABLE IS TABLE OF USER_TAB_COLUMNS%ROWTYPE;
V_USER_TAB_COLS USER_TAB_COL_TABLE;
V_SELECTER_SQL_TEXT CLOB := '';
BEGIN
SELECT *
BULK COLLECT INTO V_USER_TAB_COLS
FROM USER_TAB_COLUMNS
WHERE USER_TAB_COLUMNS.TABLE_NAME = P_TABLE_NAME
AND USER_TAB_COLUMNS.DATA_TYPE IN ('NUMBER');
P_COLUMN_LIST := P_COLUMN_LIST || V_USER_TAB_COLS(1).COLUMN_NAME;
V_SELECTER_SQL_TEXT := V_SELECTER_SQL_TEXT || V_USER_TAB_COLS(1).COLUMN_NAME;
FOR POINTER IN 2..V_USER_TAB_COLS.COUNT
LOOP
P_COLUMN_LIST := P_COLUMN_LIST || ',' || V_USER_TAB_COLS(POINTER).COLUMN_NAME;
V_SELECTER_SQL_TEXT := V_SELECTER_SQL_TEXT || Q'!||','|| !' || V_USER_TAB_COLS(POINTER).COLUMN_NAME;
END LOOP;
V_SELECTER_SQL_TEXT := UTL_LMS.FORMAT_MESSAGE(Q'!SELECT LISTAGG('INSERT INTO %s !', P_TABLE_NAME) || '(' || P_COLUMN_LIST || Q'!) VALUES ( '||!' || V_SELECTER_SQL_TEXT || UTL_LMS.FORMAT_MESSAGE(Q'!||');'||CHR(10)||CHR(13) ) WITHIN GROUP (ORDER BY %s ASC) FROM !', P_COLUMN_NAME) || P_TABLE_NAME || ' WHERE ' || P_COLUMN_NAME || ' = ' || P_COLUMN_VALUE;
EXECUTE IMMEDIATE V_SELECTER_SQL_TEXT INTO V_DATA_DML;
END;
/
然后尝试T1/C1
:
DECLARE
V_RESULT CLOB;
BEGIN
P_EXPORT_DATA_DML('T1','C1',10,V_RESULT);
DBMS_OUTPUT.PUT_LINE(V_RESULT);
END;
/
INSERT INTO T1 (C1,C2) VALUES ( 10,20);
INSERT INTO T1 (C1,C2) VALUES ( 10,21);
PL/SQL procedure successfully completed.
或T2/C1
:
DECLARE
V_RESULT CLOB;
BEGIN
P_EXPORT_DATA_DML('T2','C1',10,V_RESULT);
DBMS_OUTPUT.PUT_LINE(V_RESULT);
END;
/
INSERT INTO T2 (C1,C2,C3) VALUES ( 10,20,30);
INSERT INTO T2 (C1,C2,C3) VALUES ( 10,21,31);
PL/SQL procedure successfully completed.
或T2/C2
:
...
P_EXPORT_DATA_DML('T2','C2',20,V_RESULT);
...
INSERT INTO T2 (C1,C2,C3) VALUES ( 10,20,30);
PL/SQL procedure successfully completed.
要支持其他DATA_TYPE
,您需要处理DATE/TIMESTAMP -> CHAR
次转化,引用CHAR
等。
以下是支持NUMBER + VARCHAR2
CREATE OR REPLACE PROCEDURE P_EXPORT_DATA_DML(P_TABLE_NAME IN VARCHAR2, P_COLUMN_NAME IN VARCHAR2, P_COLUMN_VALUE IN NUMBER, V_DATA_DML OUT CLOB)
IS
P_COLUMN_LIST CLOB;
V_VALUES_LIST CLOB;
TYPE USER_TAB_COL_TABLE IS TABLE OF USER_TAB_COLUMNS%ROWTYPE;
V_USER_TAB_COLS USER_TAB_COL_TABLE;
V_SELECTER_SQL_TEXT CLOB := '';
BEGIN
SELECT *
BULK COLLECT INTO V_USER_TAB_COLS
FROM USER_TAB_COLUMNS
WHERE USER_TAB_COLUMNS.TABLE_NAME = P_TABLE_NAME
AND USER_TAB_COLUMNS.DATA_TYPE IN ('NUMBER', 'VARCHAR2');
P_COLUMN_LIST := P_COLUMN_LIST || V_USER_TAB_COLS(1).COLUMN_NAME;
CASE WHEN V_USER_TAB_COLS(1).DATA_TYPE = 'NUMBER'
THEN
V_SELECTER_SQL_TEXT := V_SELECTER_SQL_TEXT || V_USER_TAB_COLS(1).COLUMN_NAME;
WHEN V_USER_TAB_COLS(1).DATA_TYPE = 'VARCHAR2'
THEN
V_SELECTER_SQL_TEXT := V_SELECTER_SQL_TEXT || Q'!''''||!' || V_USER_TAB_COLS(1).COLUMN_NAME || Q'!||''''!';
END CASE;
FOR POINTER IN 2..V_USER_TAB_COLS.COUNT
LOOP
P_COLUMN_LIST := P_COLUMN_LIST || ',' || V_USER_TAB_COLS(POINTER).COLUMN_NAME;
CASE WHEN V_USER_TAB_COLS(POINTER).DATA_TYPE = 'NUMBER'
THEN
V_SELECTER_SQL_TEXT := V_SELECTER_SQL_TEXT || Q'!||','|| !' || V_USER_TAB_COLS(POINTER).COLUMN_NAME;
WHEN V_USER_TAB_COLS(POINTER).DATA_TYPE = 'VARCHAR2'
THEN
V_SELECTER_SQL_TEXT := V_SELECTER_SQL_TEXT || Q'!||','|| !' || Q'!''''||!' || V_USER_TAB_COLS(POINTER).COLUMN_NAME || Q'!||''''!';
END CASE;
END LOOP;
V_SELECTER_SQL_TEXT := UTL_LMS.FORMAT_MESSAGE(Q'!SELECT LISTAGG('INSERT INTO %s !', P_TABLE_NAME) || '(' || P_COLUMN_LIST || Q'!) VALUES ( '||!' || V_SELECTER_SQL_TEXT || UTL_LMS.FORMAT_MESSAGE(Q'!||');'||CHR(10)||CHR(13) ) WITHIN GROUP (ORDER BY %s ASC) FROM !', P_COLUMN_NAME) || P_TABLE_NAME || ' WHERE ' || P_COLUMN_NAME || ' = ' || P_COLUMN_VALUE;
EXECUTE IMMEDIATE V_SELECTER_SQL_TEXT INTO V_DATA_DML;
END;
/
使用VARCHAR2
表格进行测试:
CREATE TABLE T3 (C1 VARCHAR2(64), C2 NUMBER, C3 VARCHAR2(64));
INSERT INTO T3 VALUES ('XX',10,'AA');
INSERT INTO T3 VALUES ('XQ',10,'AQ');
INSERT INTO T3 VALUES ('XX',20,'AA');
获取多行:
DECLARE
V_RESULT CLOB;
BEGIN
P_EXPORT_DATA_DML('T3','C2',10,V_RESULT);
DBMS_OUTPUT.PUT_LINE(V_RESULT);
END;
/
INSERT INTO T3 (C1,C2,C3) VALUES ( 'XQ',10,'AQ');
INSERT INTO T3 (C1,C2,C3) VALUES ( 'XX',10,'AA');
PL/SQL procedure successfully completed.
答案 1 :(得分:1)
尝试此程序。请注意,它适用于NUMBER,VARCHAR,DATE(具有默认格式时)数据类型
set serveroutput on
create or replace
procedure p_export_data_dml(p_table in varchar2, p_filter_column in varchar2, p_filter_value in varchar2, p_dmls in out varchar2)
is
cursor c_statements(p_table varchar2, p_filter_column varchar2, p_filter_value varchar2) is
select 'select ''insert into '||p_table||' ('||
listagg(column_name, ', ') within group (order by column_id) ||') values(''''''||'||
listagg(column_name, '||'''''', ''''''||') within group (order by column_id)||'||'''''');'' from '||p_table||' where '||p_filter_column||' = '''||p_filter_value||'''' insert_statement
from user_tab_columns
where table_name = upper(p_table);
v_output varchar2(4000);
v_sql varchar2(4000);
type t_cursor is ref cursor;
c_cur t_cursor;
begin
for r_statements in c_statements(p_table, p_filter_column, p_filter_value) loop
v_sql := r_statements.insert_statement;
dbms_output.put_line(v_sql);
open c_cur for v_sql;
loop
fetch c_cur into v_output;
exit when c_cur%notfound;
if p_dmls = null then
p_dmls := v_output;
else
p_dmls := p_dmls || '
'||v_output;
end if;
end loop;
close c_cur;
end loop;
end;
/
然后您可以尝试使用以下
执行declare
v_text varchar2(32000);
begin
p_export_data_dml('t1', 'c1', 10, v_text);
dbms_output.put_line(v_text);
end;
/
输出可以是这样的:
insert into t1 (C1, C2, C3, C4) values('10', '1', '11', '12-SEP-2017 07:54:38');
insert into t1 (C1, C2, C3, C4) values('10', '2', '12', '12-SEP-2017 07:54:38');
insert into t1 (C1, C2, C3, C4) values('10', '3', '13', '12-SEP-2017 07:54:38');