有什么方法可以模拟在 PL/SQL 中的语句触发器中使用行触发器的 :old 和 :new 的方式
Is there any way we can emulate the way of using :old and :new of row triggers in statement triggers in PL/SQL
朋友们,你们好!
所以,这是我的问题,上面写着 -
Write a update, delete trigger on clientmstr table. The System
should keep track of the records that ARE BEING updated or
deleted. The old value of updated or deleted records should be
added in audit_trade table. (Separate implementation using both row
and statement triggers)
我的解决方案是这样的 -
-- For row trigger
create or replace trigger row_trigger
before delete or update on client_master
referencing old as old new as new
for each row
begin
insert into audit_table values(
:old.client_id, :old.client_name, :old.client_budget
);
end;
/
根据这个问题,我必须使用 语句触发器 来实现相同的功能,但我想不出可以完成的方法。我研究了语句触发器,我了解到我们不能在这里使用 :old 和 :new 。 有什么方法可以实现使用语句触发器在审计 table 中添加相同的行触发器方法吗? 我才刚刚开始,才两天我就开始学习 PL/SQL。我花了一整天在互联网上到处搜索 - 试图寻找一个例子,但我没有得到它。有人可以帮忙吗?
编辑
(1) 我正在使用 Oracle SQL 开发人员
(2) 正如有人在评论中建议的那样 - 在语句触发器中不可能这样做,我也这么认为。这个星期六我必须交作业。我和我的老师谈过 - 她说可以使用语句触发器来实现它。我问她如何——但她没有回应。然后我问她提示,她说了这个(我正在复制粘贴她的文字)-
Create a separate table with col as operations and timestamp. Write statement level trigger on insert update and delete operations. The trigger will capture the operation fired and timestamp by inserting values in table.
我不明白那是什么意思或如何去做!谁能帮我解决这个问题?
您可以使用复合触发器。
创建类型:
CREATE TYPE client_master_obj IS OBJECT(
id NUMBER,
name VARCHAR2(20),
budget NUMBER(10,2)
);
CREATE TYPE client_master_table IS TABLE OF client_master_obj;
然后触发器:
CREATE TRIGGER client_master_cmp_trigger
FOR DELETE OR UPDATE ON client_master
COMPOUND TRIGGER
data client_master_table := client_master_table();
AFTER EACH ROW
IS
BEGIN
data.EXTEND(1);
data(data.COUNT) := client_master_obj(
:OLD.client_id,
:OLD.client_name,
:OLD.client_budget
);
END AFTER EACH ROW;
AFTER STATEMENT
IS
BEGIN
INSERT INTO audit_table (client_id, client_name, client_budget, trg_type)
SELECT id,
name,
budget,
'C'
FROM TABLE(data);
END AFTER STATEMENT;
END;
/
其中,对于示例数据:
CREATE TABLE client_master (client_id, client_name, client_budget) AS
SELECT 1, 'Alice', 100 FROM DUAL UNION ALL
SELECT 2, 'Beryl', 200 FROM DUAL UNION ALL
SELECT 3, 'Carol', 300 FROM DUAL UNION ALL
SELECT 4, 'Debra', 400 FROM DUAL UNION ALL
SELECT 5, 'Emily', 500 FROM DUAL;
CREATE TABLE audit_table (client_id, client_name, client_budget, trg_type) AS
SELECT cm.*, 'X' FROM client_master cm WHERE 1 = 0;
之后:
UPDATE client_master
SET client_budget = client_budget + 600
WHERE client_id IN (1, 2);
DELETE FROM client_master WHERE client_id IN (1, 3);
然后审计 table 包含(行触发器也触发相同的更改):
SELECT * FROM audit_table;
CLIENT_ID
CLIENT_NAME
CLIENT_BUDGET
TRG_TYPE
1
Alice
100
R
2
Beryl
200
R
1
Alice
100
C
2
Beryl
200
C
1
Alice
700
R
3
Carol
300
R
1
Alice
700
C
3
Carol
300
C
db<>fiddle here
使用复合触发器的相同方法,但虽然不是字面上的级别语句触发器,因为通常它们指的是 table 级别触发器。
create or replace trigger row_compound_trigger
for delete or update on client_master
compound trigger
--
-- an array structure to buffer all the row changes
--
type t_row_list is
table of client_master%rowtype index by pls_integer;
l_audit_rows t_row_list;
l_operation varchar2(1) :=
case when updating then 'U'
when deleting then 'D'
end;
before statement is
begin
--
-- initialize the array
--
l_audit_rows.delete;
end before statement;
after each row is
begin
--
-- at row level, capture all the changes into the array
-- this variables use sys_context in case you want to use it ( not needed )
--
l_audit_rows(l_audit_rows.count+1).aud_who := sys_context('USERENV','SESSION_USER');
l_audit_rows(l_audit_rows.count).aud_when := sysdate;
l_audit_rows(l_audit_rows.count).aud_operation := l_operation;
l_audit_rows(l_audit_rows.count).aud_module := sys_context('USERENV','MODULE');
if updating then
l_audit_rows(l_audit_rows.count).client_id := :new.client_id
l_audit_rows(l_audit_rows.count).client_name := :new.client_name
... all the fields
else
l_audit_rows(l_audit_rows.count).client_id := :old.client_id
l_audit_rows(l_audit_rows.count).client_name := :old.client_name
... all the fields
end if;
end after each row;
after statement is
begin
--
-- then at completion, do a single insert of all the rows into our audit table
--
forall i in 1 .. l_audit_rows.count
insert into audit_table values l_audit_rows(i);
l_audit_rows.delete;
end after statement;
end;
/
朋友们,你们好!
所以,这是我的问题,上面写着 -
Write a update, delete trigger on clientmstr table. The System should keep track of the records that ARE BEING updated or deleted. The old value of updated or deleted records should be added in audit_trade table. (Separate implementation using both row and statement triggers)
我的解决方案是这样的 -
-- For row trigger
create or replace trigger row_trigger
before delete or update on client_master
referencing old as old new as new
for each row
begin
insert into audit_table values(
:old.client_id, :old.client_name, :old.client_budget
);
end;
/
根据这个问题,我必须使用 语句触发器 来实现相同的功能,但我想不出可以完成的方法。我研究了语句触发器,我了解到我们不能在这里使用 :old 和 :new 。 有什么方法可以实现使用语句触发器在审计 table 中添加相同的行触发器方法吗? 我才刚刚开始,才两天我就开始学习 PL/SQL。我花了一整天在互联网上到处搜索 - 试图寻找一个例子,但我没有得到它。有人可以帮忙吗?
编辑
(1) 我正在使用 Oracle SQL 开发人员
(2) 正如有人在评论中建议的那样 - 在语句触发器中不可能这样做,我也这么认为。这个星期六我必须交作业。我和我的老师谈过 - 她说可以使用语句触发器来实现它。我问她如何——但她没有回应。然后我问她提示,她说了这个(我正在复制粘贴她的文字)-
Create a separate table with col as operations and timestamp. Write statement level trigger on insert update and delete operations. The trigger will capture the operation fired and timestamp by inserting values in table.
我不明白那是什么意思或如何去做!谁能帮我解决这个问题?
您可以使用复合触发器。
创建类型:
CREATE TYPE client_master_obj IS OBJECT(
id NUMBER,
name VARCHAR2(20),
budget NUMBER(10,2)
);
CREATE TYPE client_master_table IS TABLE OF client_master_obj;
然后触发器:
CREATE TRIGGER client_master_cmp_trigger
FOR DELETE OR UPDATE ON client_master
COMPOUND TRIGGER
data client_master_table := client_master_table();
AFTER EACH ROW
IS
BEGIN
data.EXTEND(1);
data(data.COUNT) := client_master_obj(
:OLD.client_id,
:OLD.client_name,
:OLD.client_budget
);
END AFTER EACH ROW;
AFTER STATEMENT
IS
BEGIN
INSERT INTO audit_table (client_id, client_name, client_budget, trg_type)
SELECT id,
name,
budget,
'C'
FROM TABLE(data);
END AFTER STATEMENT;
END;
/
其中,对于示例数据:
CREATE TABLE client_master (client_id, client_name, client_budget) AS
SELECT 1, 'Alice', 100 FROM DUAL UNION ALL
SELECT 2, 'Beryl', 200 FROM DUAL UNION ALL
SELECT 3, 'Carol', 300 FROM DUAL UNION ALL
SELECT 4, 'Debra', 400 FROM DUAL UNION ALL
SELECT 5, 'Emily', 500 FROM DUAL;
CREATE TABLE audit_table (client_id, client_name, client_budget, trg_type) AS
SELECT cm.*, 'X' FROM client_master cm WHERE 1 = 0;
之后:
UPDATE client_master
SET client_budget = client_budget + 600
WHERE client_id IN (1, 2);
DELETE FROM client_master WHERE client_id IN (1, 3);
然后审计 table 包含(行触发器也触发相同的更改):
SELECT * FROM audit_table;
CLIENT_ID CLIENT_NAME CLIENT_BUDGET TRG_TYPE 1 Alice 100 R 2 Beryl 200 R 1 Alice 100 C 2 Beryl 200 C 1 Alice 700 R 3 Carol 300 R 1 Alice 700 C 3 Carol 300 C
db<>fiddle here
使用复合触发器的相同方法,但虽然不是字面上的级别语句触发器,因为通常它们指的是 table 级别触发器。
create or replace trigger row_compound_trigger
for delete or update on client_master
compound trigger
--
-- an array structure to buffer all the row changes
--
type t_row_list is
table of client_master%rowtype index by pls_integer;
l_audit_rows t_row_list;
l_operation varchar2(1) :=
case when updating then 'U'
when deleting then 'D'
end;
before statement is
begin
--
-- initialize the array
--
l_audit_rows.delete;
end before statement;
after each row is
begin
--
-- at row level, capture all the changes into the array
-- this variables use sys_context in case you want to use it ( not needed )
--
l_audit_rows(l_audit_rows.count+1).aud_who := sys_context('USERENV','SESSION_USER');
l_audit_rows(l_audit_rows.count).aud_when := sysdate;
l_audit_rows(l_audit_rows.count).aud_operation := l_operation;
l_audit_rows(l_audit_rows.count).aud_module := sys_context('USERENV','MODULE');
if updating then
l_audit_rows(l_audit_rows.count).client_id := :new.client_id
l_audit_rows(l_audit_rows.count).client_name := :new.client_name
... all the fields
else
l_audit_rows(l_audit_rows.count).client_id := :old.client_id
l_audit_rows(l_audit_rows.count).client_name := :old.client_name
... all the fields
end if;
end after each row;
after statement is
begin
--
-- then at completion, do a single insert of all the rows into our audit table
--
forall i in 1 .. l_audit_rows.count
insert into audit_table values l_audit_rows(i);
l_audit_rows.delete;
end after statement;
end;
/