这是我在Table_A上的插入触发器,我将参数存储到系统中。当我插入表格时,我想更改最后一条记录的end_date以保持记录版本。
create or replace trigger parameter_version
before insert
on parameters
for each row
declare
v_is_exist number := 0;
v_rowid rowid;
begin
select count(*) into v_is_exist from parameters where name = :new.name; -- check if parameter exist
select rowid into v_rowid from parameters where name = :new.name and end_date is null; -- record rowid, which sholud be changed
if v_is_exist <> 0 then
set end_date = :new.start_date - 1
end if;
end;
插入前表中的情况是:
| id | name | value | start_date | end_date |
-----------------------------------------------
| 1 |Par_A | 10 | 2016-09-01 | 2016-10-01 |
-----------------------------------------------
| 2 |Par_A | 20 | 2016-10-02 | 2016-10-03 |
-----------------------------------------------
| 3 |Par_A | 30 | 2016-10-05 | <null> |
-----------------------------------------------
id = 3的记录应该设置end_date:new_start_date - 1(关闭版本)并且在插入记录时我有一个下一个param版本,其中包含start_date = sysdate。
我有一个ORA-04091错误'表名正在变异,触发/功能可能看不到它。'
我知道这个案子很难,可能不可能但也许有人知道解决方案? 或者可能存在另一种解决方案?
答案 0 :(得分:1)
您可以使用带有LEAD分析功能的After Statement触发器来处理此问题:
DROP TABLE demo;
CREATE TABLE demo( id NUMBER
, name VARCHAR2( 30 )
, VALUE NUMBER
, start_date DATE
, end_date DATE
);
INSERT INTO demo( id, name, VALUE, start_date, end_date )
VALUES ( 1, 'Par_A', 10, TO_DATE( '2016-09-01', 'YYYY-MM-DD' ), TO_DATE( '2016-10-01', 'YYYY-MM-DD' ) );
INSERT INTO demo( id, name, VALUE, start_date, end_date )
VALUES ( 2, 'Par_A', 20, TO_DATE( '2016-10-02', 'YYYY-MM-DD' ), TO_DATE( '2016-10-04', 'YYYY-MM-DD' ) );
INSERT INTO demo( id, name, VALUE, start_date )
VALUES ( 3, 'Par_A', 30, TO_DATE( '2016-10-05', 'YYYY-MM-DD' ) );
INSERT INTO demo( id, name, VALUE, start_date )
VALUES ( 4, 'Par_A', 40, TO_DATE( '2016-10-07', 'YYYY-MM-DD' ) );
INSERT INTO demo( id, name, VALUE, start_date )
VALUES ( 5, 'Par_A', 50, TO_DATE( '2016-10-11', 'YYYY-MM-DD' ) );
COMMIT;
SELECT id
, name
, start_date
, end_date
, LEAD( start_date ) OVER( PARTITION BY name ORDER BY start_date ) - 1 AS new_date
FROM demo
WHERE end_date IS NULL
ORDER BY id;
CREATE OR REPLACE TRIGGER demo_aius
AFTER INSERT OR UPDATE
ON demo
REFERENCING NEW AS new OLD AS old
DECLARE
CURSOR c_todo
IS
SELECT id, new_date
FROM (SELECT id
, name
, start_date
, end_date
, LEAD( start_date ) OVER( PARTITION BY name ORDER BY start_date ) - 1 AS new_date
FROM demo
WHERE end_date IS NULL)
WHERE new_date IS NOT NULL;
BEGIN
FOR rec IN c_todo
LOOP
UPDATE demo
SET end_date = rec.new_date
WHERE id = rec.id;
END LOOP;
END demo_aius;
/
INSERT INTO demo( id, name, VALUE, start_date )
VALUES ( 6, 'Par_A', 60, TO_DATE( '2016-10-15', 'YYYY-MM-DD' ) );
COMMIT;
SELECT id
, name
, start_date
, end_date
FROM demo
ORDER BY id;
与脚本显示类似,如果触发器被意外禁用,这样的更新甚至可以处理多个丢失的结束日期。 &#34; PARTITION BY名称&#34; part确保它在复杂的insert语句之后也起作用。
顺便说一下,我同意触发器中的自主交易是最后的选择。我试图通过控制用户界面并将所有这些功能放在包中来避免触发器。
答案 1 :(得分:0)
尝试这样的事情:
create or replace trigger parameter_version
before insert
on parameters
for each row
begin
/*Don't care if there's 0 rows updated */
update parameters
set end_date = :new.start_date - 1
where name = :new.name and end_date is null;
:new.end_date := null;
end;