插入上的 Oracle 变异触发器
Posted
技术标签:
【中文标题】插入上的 Oracle 变异触发器【英文标题】:Oracle mutating trigger on INSERT 【发布时间】:2018-04-17 11:08:14 【问题描述】:我正在尝试在 Oracle 8i 中创建一个包含 3 个表的小型数据库,并在其中创建两个触发器。 这是数据库架构:
我已经创建了表格:
CREATE TABLE SYSTEM.Invoices(
invoice_id NUMBER NOT NULL,
invoice_body_xml CLOB NOT NULL,
insertTS DATE NOT NULL,
modifyTS DATE,
PRIMARY KEY (invoice_id))
TABLESPACE SYSTEM;
CREATE TABLE SYSTEM.Invoice_Statuses(
invoice_id NUMBER NOT NULL,
status NVARCHAR2(15) NOT NULL,
status_details CLOB,
transaction_id NVARCHAR2(50),
transaction_index NUMBER,
request_id NVARCHAR2(50),
insertTS DATE NOT NULL,
CONSTRAINT from_statuses_to_invoices
FOREIGN KEY(invoice_id)
REFERENCES SYSTEM.INVOICES(invoice_id))
TABLESPACE SYSTEM;
CREATE TABLE SYSTEM.Open_Invoices(
invoice_id NUMBER NOT NULL,
invoice_body_xml CLOB NOT NULL,
status NVARCHAR2(15) NOT NULL,
transaction_id NVARCHAR2(50),
transaction_index NUMBER,
insertTS DATE NOT NULL,
CONSTRAINT from_open_to_invoices
FOREIGN KEY(invoice_id)
REFERENCES SYSTEM.INVOICES(invoice_id))
TABLESPACE SYSTEM;
还有我需要的触发器:
CREATE OR REPLACE TRIGGER after_invoice_insert
AFTER INSERT
ON SYSTEM.INVOICES
FOR EACH ROW
BEGIN
INSERT INTO SYSTEM.INVOICE_STATUSES
(INVOICE_ID,
STATUS,
INSERTTS)
VALUES
(
:NEW.invoice_id,
n'NEW',
SYSDATE);
END;
还有一个:
CREATE OR REPLACE TRIGGER after_invoice_statuses_insert
AFTER INSERT
ON SYSTEM.INVOICE_STATUSES
FOR EACH ROW
DECLARE
body_xml CLOB;
BEGIN
SELECT SYSTEM.INVOICES.invoice_body_xml INTO body_xml FROM SYSTEM.INVOICES WHERE SYSTEM.INVOICES.invoice_id = :NEW.invoice_id;
INSERT INTO SYSTEM.OPEN_INVOICES
(INVOICE_ID,
INVOICE_BODY_XML,
STATUS,
TRANSACTION_ID,
TRANSACTION_INDEX,
INSERTTS)
VALUES
(
:NEW.invoice_id,
body_xml,
:NEW.status,
:NEW.transaction_id,
:NEW.transaction_index,
SYSDATE);
END;
如您所见,在 OPEN_INVOICES 表中,我需要来自 INVOICES 表的 body_xml,这就是为什么我要使用 select 创建 body_xml。
在此之后,当我尝试插入发票时,我收到此错误:
【问题讨论】:
Oracle 8i - 真的吗? 20岁了! 您不应在SYSTEM
架构中创建任何用户对象。
@WernfriedDomscheit 没有帮助。如果您发布此内容,请解释为什么不使用 SYSTEM 或使用 cmets 要求澄清。
对,但是使用 SYSTEM 是 MCVE 的一部分(这是一个保留的内部 Oracle 帐户,具有提升的权限,不适用于用户定义的对象)。
从不,永远使用 SYS 或 SYSTEM 用户创建您自己的表。只是不要
【参考方案1】:
您有一个在插入后触发 INVOICES
的触发器。这会触发插入到INVOICE_STATUSES
。
您还有一个在插入后触发 INVOICE_STATUSES
的触发器,但是在此触发器中,您尝试从表 INVOICES
中进行选择 - 出现错误。
将您的语句和任何触发器中的所有 DML 视为一个 命令。当您在其中插入任何数据时,您不能选择一个表。
您应该将所有逻辑放入一个存储过程并执行该过程。
【讨论】:
【参考方案2】:如果您需要通过触发器完成此操作,那么最好的方法可能是将invoice_body_xml
也存储在INVOICE_STATUSES
中,并将其填充到INVOICES
的触发器中:
CREATE OR REPLACE TRIGGER after_invoice_insert
AFTER INSERT
ON invoices
FOR EACH ROW
BEGIN
INSERT INTO invoice_statuses
( invoice_id, status, insertts, invoice_body_xml )
VALUES
( :new.invoice_id, n'NEW', SYSDATE, :new.invoice_body_xml );
END;
/
CREATE OR REPLACE TRIGGER after_invoice_statuses_insert
AFTER INSERT
ON invoice_statuses
FOR EACH ROW
BEGIN
INSERT INTO open_invoices
( invoice_id, invoice_body_xml, status, transaction_id, transaction_index, insertts )
VALUES
( :new.invoice_id, :new.invoice_body_xml, :new.status, :new.transaction_id, :new.transaction_index, SYSDATE );
END;
/
【讨论】:
【参考方案3】:我建议不要尝试折叠、旋转和破坏一堆触发器来执行所需的 INSERT、SELECT 等操作,我建议您编写一个类似于以下内容的程序来创建您的发票:
CREATE OR REPLACE PROCEDURE CREATE_INVOICE
(pinInvoice_id IN NUMBER,
pinInvoice_body_xml IN CLOB)
IS
BEGIN
INSERT INTO INVOICES
(INVOICE_ID,
INVOICE_BODY,
INSERTTS)
VALUES
(pinInvoice_id,
pinInvoice_body_xml,
SYSDATE);
INSERT INTO INVOICE_STATUSES
(INVOICE_ID,
STATUS,
INSERTTS)
VALUES
(pinInvoice_id,
n'NEW',
SYSDATE);
INSERT INTO OPEN_INVOICES
(INVOICE_ID,
INVOICE_BODY_XML,
STATUS,
TRANSACTION_ID,
TRANSACTION_INDEX,
INSERTTS)
VALUES
(pinInvoice_id,
pinInvoice_body_xml,
n'NEW',
???, -- don't know where this comes from
???, -- don't know where this comes from
SYSDATE);
END CREATE_INVOICE;
您可能仍想使用触发器来设置 INSERTTS 和 MODIFYTS 等字段。
在执行此操作时,我发现需要设置 OPEN_INVOICES 中的几个字段,但是(据我所知)它们没有被初始化。这可能是您想要调查的内容。
祝你好运。
【讨论】:
以上是关于插入上的 Oracle 变异触发器的主要内容,如果未能解决你的问题,请参考以下文章
Oracle - 在没有触发器的情况下更新表时出现“表正在变异,触发器/函数可能看不到它”错误