Create trigger to only populate history table if record doesnt exist - triggers

Need some help.
I need to update a trigger that already exists that when a new record is updated, the previous record is inserted in an history table.
Current trigger:
create or replace TRIGGER "AUDIT"
BEFORE UPDATE OF STATUS,UPDATE_DATE,CREATED_DATE,PREMISES_ID ON "PREMISES"
REFERENCING OLD AS OLD_PREMISES NEW AS NEW_PREMISES
FOR EACH ROW
BEGIN
INSERT INTO PREMISES_H
(
ID,
PREMISES_ID,
STATUS,
UPDATE_DATE,
CREATED_DATE
)
VALUES (
EDB_PREMISES_HISTORY_SEQ.nextval,
:OLD_PREMISES.PREMISES_ID,
:OLD_PREMISES.STATUS,
:OLD_PREMISES.UPDATE_DATE,
:OLD_PREMISES.CREATED_DATE
);
END;
Before inserting into PREMISES_H I need it to check if it already exists if it does just updates the UPDATE_DATE
create or replace TRIGGER "AUDIT"
BEFORE UPDATE OF STATUS,LAST_UPDATE_DATE,CREATED_DATE,PREMISES_ID ON "PREMISES"
REFERENCING OLD AS OLD_PREMISES NEW AS NEW_PREMISES
FOR EACH ROW
BEGIN
IF EXISTS (SELECT * FROM PREMISES_H WHERE PREMISES_ID = :OLD_PREMISES.PREMISES_ID AND STATUS = :OLD_PREMISES.STATUS)
THEN UPDATE PREMISES_H SET UPDATE_DATE = :OLD_PREMISES.UPDATE_DATE WHERE PREMISES_ID = :OLD_PREMISES.PREMISES_ID AND ELIGIBILITY_STATUS = :OLD_PREMISES.STATUS;
ELSE INSERT INTO PREMISES_H
(
ID,
PREMISES_ID,
STATUS,
LAST_UPDATE_DATE,
CREATED_DATE
)
VALUES (
EDB_PREMISES_HISTORY_SEQ.nextval,
:OLD_PREMISES.PREMISES_ID,
:OLD_PREMISES.STATUS,
:OLD_PREMISES.LAST_UPDATE_DATE,
:OLD_PREMISES.CREATED_DATE
);
END;
Would this be the best approach?
Thank you
I was expecting to no insert if the record from premises already exists in premises_h

Related

trigger to set date automatic after update

Some background info: i have a table named defects which has column named status_id and another column named date_closed ,i want to set date_closed after status_id has been updated
i already try to do this using after update trigger with the following code:
after update on eba_bt_sw_defects
for each row
declare
l_status number(20) := null;
begin
select status_id into l_status from eba_bt_sw_defects D,eba_bt_status S where D.status_id = S.id;
if l_status in ( select id from eba_bt_status where is_open = 'N' and NVL(is_enhancement,'N')='N') then
:NEW.DATE_CLOSED := LOCALTIMESTAMP ;
end if;
end;
but an error occured ( subquery not allowed in this contextCompilation failed)
i want a help
A couple of things that need fixing in your code:
In a trigger do not select from the table the trigger you're on. This will probably raise a ORA-04091: table name is mutating, trigger/function may not see it error.
IF l_variable IN (SELECT ...) is not a valid oracle syntax. It raises PLS-00405: subquery not allowed in this context
I don't have your data so here is a similar example:
drop table todos;
drop table statuses;
-- create tables
create table statuses (
id number generated by default on null as identity
constraint statuses_id_pk primary key,
status varchar2(60 char),
is_open varchar2(1 char) constraint statuses_is_open_ck
check (is_open in ('Y','N'))
)
;
create table todos (
id number generated by default on null as identity
constraint todos_id_pk primary key,
name varchar2(255 char) not null,
close_date timestamp with local time zone,
status_id number
constraint todos_status_id_fk
references statuses on delete cascade
)
;
-- load data
insert into statuses (id, status, is_open ) values (1, 'OPEN', 'Y' );
insert into statuses (id, status, is_open ) values (2, 'COMPLETE', 'N' );
insert into statuses (id, status, is_open ) values (3, 'ON HOLD', 'Y' );
insert into statuses (id, status, is_open ) values (4, 'CANCELLED', 'N' );
commit;
insert into todos (name, close_date, status_id ) values ( 'Y2 Security Review', NULL, 1 );
-- triggers
CREATE OR REPLACE TRIGGER todos_biu BEFORE
INSERT OR UPDATE ON todos
FOR EACH ROW
DECLARE
l_dummy NUMBER;
BEGIN
SELECT
1
INTO l_dummy
FROM
statuses
WHERE
is_open = 'N' AND
id = :new.status_id;
:new.close_date := localtimestamp;
EXCEPTION
WHEN no_data_found THEN
-- I'm assuming you want close_date to NULL if todo is re-opened.
:new.close_date := NULL;
END todos_biu;
/
update todos set status_id = 2;
select * from todos;
id name close_date status_id
1 Y2 Security Review 11-MAY-22 05.27.04.987117000 PM 2

How to remove Firebird's triggers created automatically [duplicate]

I have a Firebird table like this:
CREATE TABLE events (
event VARCHAR(6) NOT NULL
CHECK (event IN ('deploy', 'revert', 'fail')),
change_id CHAR(40) NOT NULL,
change VARCHAR(512) NOT NULL
);
Now I need to add another value to the IN() list in the CHECK constraint. How do I do that?
Things I've tried so far:
Updating the value in RDB$TRIGGERS.RDB$TRIGGER_SOURCE:
UPDATE RDB$TRIGGERS
SET RDB$TRIGGER_SOURCE = 'CHECK (event IN (''deploy'', ''revert'', ''fail'', ''merge''))'
WHERE RDB$TRIGGER_SOURCE = 'CHECK (event IN (''deploy'', ''revert'', ''fail''))';
Does not seem to work, as the trigger is compiled in RDB$TRIGGERS.RDB$TRIGGER_BLR.
Creating a new table with a new check, copying the data over, dropping the old table and renaming the new table. However, it seems that one cannot rename a Firebird table, so I can't make the new table have the same name as the old one.
I suspect updating RDB$TRIGGERS is the way to go (idk!), if only I could get Firebird to recompile the code. But maybe there's a better way?
You need to drop and the re-create the check constraint.
As you didn't specify a name for your constraint, Firebird created one, so you first need to find that name:
select trim(cc.rdb$constraint_name), trg.rdb$trigger_source
from rdb$relation_constraints rc
join rdb$check_constraints cc on rc.rdb$constraint_name = cc.rdb$constraint_name
join rdb$triggers trg on cc.rdb$trigger_name = trg.rdb$trigger_name
where rc.rdb$relation_name = 'EVENTS'
and rc.rdb$constraint_type = 'CHECK'
and trg.rdb$trigger_type = 1;
I just added the trigger source for informational reasons.
Once you have the name, you can drop it, e.g.
alter table events drop constraint integ_27;
and then add the new constraint:
alter table events
add constraint check_event_type
CHECK (event IN ('deploy', 'revert', 'fail', 'merge'));
In the future you don't need to look for the constraint name because you already it.
Here's how to do it dynamically:
SET AUTOddl OFF;
SET TERM ^;
EXECUTE BLOCK AS
DECLARE trig VARCHAR(64);
BEGIN
SELECT TRIM(cc.rdb$constraint_name)
FROM rdb$relation_constraints rc
JOIN rdb$check_constraints cc ON rc.rdb$constraint_name = cc.rdb$constraint_name
JOIN rdb$triggers trg ON cc.rdb$trigger_name = trg.rdb$trigger_name
WHERE rc.rdb$relation_name = 'EVENTS'
AND rc.rdb$constraint_type = 'CHECK'
AND trg.rdb$trigger_type = 1
INTO trig;
EXECUTE STATEMENT 'ALTER TABLE EVENTS DROP CONSTRAINT ' || trig;
END^
SET TERM ;^
COMMIT;
ALTER TABLE events ADD CONSTRAINT check_event_type CHECK (
event IN ('deploy', 'revert', 'fail', 'merge')
);
COMMIT;
I had to disable AUTOddl and put in explicit commits or else I got a deadlock on the ALTER TABLE ADD CONSTRAINT statement.
Here's how to do it dynamically:
EXECUTE BLOCK RETURNS (STMT VARCHAR(1000)) AS
BEGIN
SELECT TRIM(R.RDB$CONSTRAINT_NAME)
FROM RDB$RELATION_CONSTRAINTS R
WHERE R.RDB$RELATION_NAME = 'TABLE_NAME'
AND UPPER(R.RDB$CONSTRAINT_TYPE) = UPPER('PRIMARY KEY')
INTO :STMT;
IF (:STMT IS NOT NULL) THEN
BEGIN
EXECUTE STATEMENT 'ALTER TABLE TABLE_NAME DROP CONSTRAINT ' || :STMT || ';';
EXECUTE STATEMENT 'ALTER TABLE TABLE_NAME ADD CONSTRAINT ' || :STMT || ' PRIMARY KEY (FIELD1, FIELD2, FIELD3);';
END
ELSE
BEGIN
EXECUTE STATEMENT 'ALTER TABLE FIELD1 ADD CONSTRAINT PK_PRIMARY_NAME PRIMARY KEY (FIELD1, FIELD2, FIELD3);';
END
END;

Create Trigger that Insert data after update on specific column

So i want to insert data to history_rent table and delete data in rent table after update status_peminjaman column on rent table, i am already create Trigger but it doesn't triggered
CREATE OR ALTER TRIGGER AfterUpdateStatus on dbo.peminjaman
FOR UPDATE
AS DECLARE
#nama_peminjam varchar(100),
#tanggal_pinjam datetime,
#tanggal_kemblali datetime,
#nama_guru varchar(100),
#status_peminjaman varchar(50),
#kode_barang varchar(255);
SELECT #nama_peminjam = ins.nama_peminjam FROM INSERTED ins;
SELECT #tanggal_pinjam = ins.tanggal_pinjam FROM INSERTED ins;
SELECT #tanggal_kembali = ins.tanggal_kembali FROM INSERTED ins;
SELECT #nama_guru = ins.nama_guru FROM INSERTED ins;
SELECT #kode_barang = ins.kode_barang FROM INSERTED ins;
SELECT #status_peminjaman = ins.status_peminjaman FROM INSERTED ins;
IF UPDATE(status_peminjaman)
BEGIN
SET #status_peminjaman = 'Selesai'
END
INSERT INTO dbo.history_peminjaman
VALUES(#nama_peminjam,#tanggal_pinjam,#tanggal_kembali,#nama_guru,#kode_barang,#status_peminjaman);
PRINT 'TRIGEREDDDDDDDDD'
GO

After using a trigger - ERROR: null value in column "group_id" violates not-null constraint

I'm using PostgreSQL 8.1.23 on x86_64-redhat-linux-gnu
I have to write a database for reserving seats on language courses and there's a requirement there should be a trigger, which will check whether lector, we're trying to write into new group, has any other group at the same time. I have such table:
CREATE TABLE groups (
group_id serial PRIMARY KEY,
lang varchar(3) NOT NULL,
level varchar(3),
seats int4,
lector int4,
start time,
day varchar(3),
FOREIGN KEY (language) REFERENCES languages(lang) ON UPDATE CASCADE ON DELETE CASCADE,
FOREIGN KEY (lector) REFERENCES lectors(lector_id) ON UPDATE CASCADE ON DELETE SET NULL);
and such trigger:
CREATE FUNCTION if_available () RETURNS trigger AS '
DECLARE
r groups%rowtype;
c groups%rowtype;
BEGIN
FOR r IN SELECT * FROM groups WHERE r.lector=NEW.lector ORDER BY group_id LOOP
IF (r.start = NEW.start AND r.day = NEW.day) THEN
RAISE NOTICE ''Lector already has a group at this time!'';
c = NULL;
EXIT;
ELSE
c = NEW;
END IF;
END LOOP;
RETURN c;
END;
' LANGUAGE 'plpgsql';
CREATE TRIGGER if_available_t
BEFORE INSERT OR UPDATE ON grupy
FOR EACH ROW EXECUTE PROCEDURE if_available();
After inserting the new row to a table groups, eg.:
INSERT groups (lang, level, seats, lector, start, day) values ('ger','A-2',12,2,'11:45','wed');
I get an error like this:
ERROR: null value in column "group_id" violates not-null constraint
Without this trigger everything is OK. Could anybody help me how to make it work?
Finally, I have solved it! After BEGIN there should be c = NEW;, because when table groups is empty at the beginning, FOR loop doesn't run and NULL is returned. Also I have changed the condition in FOR loop for: ...WHERE lector = NEW.lector.... And finally, I have changed the condition in IF for IF (r.group_id <> NEW.group_id AND r.start = NEW.start AND r.day = NEW.day) THEN..., because I haven't wanted to run this trigger before one particular update. Maybe this will be helpful for someone :)

Update column of a inserted row with with its generated id in a single query

Say I have a table, created as follows:
CREATE TABLE test_table (id serial, unique_id varchar(50) primary key, name varchar(50));
test_table
----------
id | unique_id | name
In that table, I would like to update the unique_id field with the newly inserted id concatenated with the inserted name in a single go.
Usually this is accomplished by two queries. (PHP way)
$q = "INSERT INTO table (unique_id,name) values ('uid','abc') returning id||name as unique_id;";
$r = pg_query($dbconn,$q);
$row = pg_fetch_array($r);
$q1 = "UPDATE test_table set unique_id =".$row['unique_id']." where unique_id='uid'";
$r1 = pg_query($dbconn,$q1);
Is there any way to do the above in a single query?
You can have several options here, you could create a AFTER trigger which uses the generated ID for an direct update of the same row:
CREATE TRIGGER test_table_insert ON AFTER INSERT ON test_table FOR EACH ROW EXECUTE PROCEDURE test_table_insert();
And in your function you update the value:
CREATE FUNCTION test_table_insert() RETURNS TRIGGER AS $$
BEGIN
UPDATE test_table SET uniqid = NEW.id::text || NEW.name WHERE id = NEW.id;
END;
$$ LANGUAGE plpgsql;
You need to add the function before the trigger.
An other option would be to do it directly in the insert:
INSERT INTO table (id, unique_id, name) values (nextval('test_table_id_seq'), 'abc', currval('test_table_id_seq')::text || 'abc') returning id;
But as a_horse_with_no_name pointed out, I think you may have a problem in your database design.