如何创建具有唯一名称的表,然后将另一个表中的数据插入该表中,最好使用单个触发器

时间:2019-01-14 06:57:21

标签: oracle plsql triggers insert

我想要一个空表,当有人向其中插入数据时,我希望该表立即复制到一个唯一的表名,然后将原始表截断以准备再次发生同样的事情。

我尝试使用触发器执行此操作,然后执行一个过程,而我尝试仅使用触发器执行此操作,但是我都遇到了问题。

CREATE OR REPLACE TRIGGER REPLICATE_PDU_TABLE
AFTER INSERT ON PDU 
DECLARE V_TABLENAME VARCHAR2(30) := 'PDU_IN_PROGRESS_ || TO_CHAR(SYSDATE, 'YYYYMMDDHHMISS');
CURSOR C1 IS
SELECT * FROM PDU;
V_PDU C1%ROWTYPE;
BEGIN 
EXECUTE IMMEDIATE 'CREATE TABLE ' || V_TABLENAME || ' AS SELECT * FROM PDU';
OPEN C1;
FETCH C1 INTO PDU ;
CLOSE C1;
INSERT INTO V_TABLENAME
VALUES (
V_PDU.TARGETSCHEMA,
V_PDU.PRODUCTIONSCHEMA,
V_PDU.PRODUCTIONDATABASE,
V_PDU.TABLE_NAME,
V_PDU.DRIVER_TABLE,
V_PDU.MANDATORY_JOIN,
V_PDU.ADDITIONAL_JOINS,
V_PDU.TABLE_COMPRESSION);
END TRIGGER REPLICATE_PDU_TABLE;

此“ May”实际上可以工作,但是我不能触发编译,因为“ INSERT INTO V_TABLENAME”将不起作用,因为该表尚不存在。这是我的代码的第一部分,创建一个唯一表,在插入后将所有数据从原始表复制到该表中。

2 个答案:

答案 0 :(得分:2)

这实际上不是我们应该在触发器中执行的操作。有一些复杂的处理过程很难管理。例如,在无法创建触发器时应该发生什么?还是如果截断失败?

此外,禁止在触发器中发布提交也使情况更加复杂。所有DDL语句均发出提交(每个语句两个),因此从触发器中触发DDL语句的唯一方法是使用autonomous_transaction编译指示。这是一个嵌套事务,这意味着它看不到表的当前内容。所以整个事情有点混乱。

这是一个思想实验,它说明了您需要做的事情:

create or replace trigger replicate_pdu_table
    after insert on pdu 
declare 
    -- note use the twenty-hour clock in mask
    v_tablename varchar2(30) := 'PDU_IN_PROGRESS_' || to_char(sysdate, 'YYYYMMDDHH24MISS');
    procedure create_table is
        pragma autonomous_transaction;
    begin
        execute immediate 'create table ' || v_tablename || ' as select * from pdu where 1=2';
    end create_table;
    procedure truncate_pdu is
        pragma autonomous_transaction;
    begin
        execute immediate 'truncate table pdu';
    end truncate_pdu;
begin 
    create_table;
    execute immediate 'insert into ' || v_tablename || ' select * from pdu';
    truncate_pdu;
end;
/

问题是被截断:它会飞奔

ORA-00054: resource busy and acquire with NOWAIT specified or timeout expired

当我们在pdu中插入行时会触发触发器。也就是说,在我们可以发出提交之前。因此,主会话锁定了pdu,因此自主事务无法进行截断。这绝对是触发器使用的障碍:您需要将其作为一个过程运行,可能是从轮询作业或通过队列调用的。


更广泛的问题是,为什么要这样做? 一种可动态创建表格的事务处理方法总是有气味的。除了泛滥的混乱模式之外,我在介绍中还提到了失败的风险。另外,使用创建表的进程将如何知道其名称?您的整个过程将基于动态SQL构建。这是维护和支持的噩梦。

如果不知道要实现的目标的详细信息,就不可能提出更好的方法。但是请放心,绝对有更好的方法可供您选择。

答案 1 :(得分:1)

还使用execute立即执行命令执行插入语句,如下所示:

CREATE OR REPLACE TRIGGER REPLICATE_PDU_TABLE
AFTER INSERT ON PDU 
DECLARE V_TABLENAME VARCHAR2(30) := 'PDU_IN_PROGRESS_ || TO_CHAR(SYSDATE, 'YYYYMMDDHHMISS');
CURSOR C1 IS
SELECT * FROM PDU;
V_PDU C1%ROWTYPE;
BEGIN 
EXECUTE IMMEDIATE 'CREATE TABLE ' || V_TABLENAME || ' AS SELECT * FROM PDU';
OPEN C1;
FETCH C1 INTO v_PDU ;
CLOSE C1;
EXECUTE IMMEDIATE '
INSERT INTO '|| V_TABLENAME || '
VALUES ( '||
V_PDU.TARGETSCHEMA ||','||
V_PDU.PRODUCTIONSCHEMA ||','||
V_PDU.PRODUCTIONDATABASE ||','||
V_PDU.TABLE_NAME ||','||
V_PDU.DRIVER_TABLE ||','||
V_PDU.MANDATORY_JOIN||','||
V_PDU.ADDITIONAL_JOINS ||','||
V_PDU.TABLE_COMPRESSION ||')';
END TRIGGER REPLICATE_PDU_TABLE;

您还可以检查以下链接,以了解如何使用execute立即语句。 Execute Immediate Example