I have a messages table. After an insert on messages, I need to insert the UserID and MsgID from that insert into the messageRecipient table. If the message was sent to a group, it needs to be inserted to every user that is a member of that group. Here is what I have, but it is not inserting into the messageRecipient table:
create or replace trigger update_messages
after insert on messages referencing new as new old as old
for each row
declare
userID1 int(10);
msgID1 int(10);
groupID1 int(10);
begin
userID1 := :new.ToUserID;
msgID1 := :new.msgID;
groupID1 := :new.ToGroupID;
if inserting then
if(userID1 <> null)
then INSERT INTO messageRecipient VALUES(msgID1, userID1);
elsif(groupID1 <> null)
THEN INSERT INTO messageRecipient(msgID, userID) SELECT msgID1, userID FROM groupMembership WHERE gID = groupID1;
end if;
end if;
end;
/
What exactly is going wrong here?
create or replace trigger update_messages
-- after insert on messages referencing new as new old as old
-- for each row
declare
userID1 int(10);
msgID1 int(10);
groupID1 int(10);
begin
userID1 := :new.ToUserID;
msgID1 := :new.msgID;
groupID1 := :new.ToGroupID;
if(userID1 is not null)
then INSERT INTO messageRecipient VALUES(msgID1, userID1);
elsif(groupID1 is not null)
THEN INSERT INTO messageRecipient(msgID, userID) SELECT msgID1, userID FROM groupMembership WHERE gID = groupID1;
end if;
end;
Comparing to null in PL/SQL will evaluate to null. And conditional statements execute only on true.
Check here for some reference.
Change userID1 <> null to userID1 is not null and groupID1 <> null to groupID1 is not null.
Also you don't need to add if inserting since this trigger is only for insert statement.
Perhaps you are checking the messageRecipient table from another session.
Remember that the trigger is part of the transaction that does the insert into messages table, therefore no changes are visible until a commit comes along.
I suggest you need to answer the question
'How do I work out what is going wrong here'
If you add an insert before your logic...
INSERT INTO messageRecipient VALUES(msgID1, userID1);
if inserting then.....
This would tell you the trigger is firing and it is picking up your new values
so it must be the comparison logic that is not working..
If you had a catch all condition you could raise an error and this would also allow you to see exactly where the problem is
if(userID1 is not null) then
INSERT INTO messageRecipient VALUES(msgID1, userID1);
elsif(groupID1 is not null) THEN
INSERT INTO messageRecipient(msgID, userID)
SELECT msgID1, userID FROM groupMembership WHERE gID = groupID1;
else
RAISE ERROR HERE
end if;
Related
I have a Row, which i wannt to Insert (or Update if the ID is already used) from another Schema with an DBLINK. This Table has many FKs, so i need to Update or Insert(if doesnt exist) around 20 Tables.
I tried it by writting all needed Rows from the different Tables from the DBLINK into Variables and then checked if the Targeted Tables had this rows already. Im sure that there is a much easier way to do it. A loop where the tables and their rowtypes are saved in an Collection with execute immdediate for example, but i couldnt find anything.
This example is like 20 times in my Code and im sure that there is an easier way to do it then writting this over and over.
PROCEDURE Test_copy (pi_id IN NUMBER) IS
V_REC_Table1 Table1%ROWTYPE;
V_REC_Table2 Table2%ROWTYPE;
V_REC_Table3 Table3%ROWTYPE;
V_REC_Table4 Table4%ROWTYPE;
V_REC_Table5 Table5%ROWTYPE;
V_REC_Table6 Table6%ROWTYPE;
V_REC_Table7 Table7%ROWTYPE;
V_REC_Table8 Table8%ROWTYPE;
V_REC_Table9 Table9%ROWTYPE;
V_REC_Table10 Table10%ROWTYPE;
V_REC_Table1_exists Table1%ROWTYPE;
V_REC_Table2_exists Table2%ROWTYPE;
V_REC_Table3_exists Table3%ROWTYPE;
V_REC_Table4_exists Table4%ROWTYPE;
V_REC_Table5_exists Table5%ROWTYPE;
V_REC_Table6_exists Table6%ROWTYPE;
V_REC_Table7_exists Table7%ROWTYPE;
V_REC_Table8_exists Table8%ROWTYPE;
V_REC_Table9_exists Table9%ROWTYPE;
V_REC_Table10_exists Table10%ROWTYPE;
v_sql VARCHAR2(2000);
Begin
v_sql := 'SELECT * FROM Table1#dblink WHERE ID = '||pi_id;
EXECUTE IMMEDIATE v_sql INTO V_REC_DBLINK_Table1;
Exception
when no_data_found then
Raise_application_Error(-20001, 'ID doesnt exist');
END;
Begin
v_sql := 'SELECT * FROM Table2#dblink WHERE ID = '||V_REC_Table1.T2_ID;
EXECUTE IMMEDIATE v_sql INTO V_REC_DBLINK_Table2;
Exception
when no_data_found then
Raise_application_Error(-20001, 'ID doesnt exist');
END;
(and so on)
.
.
.
BEGIN
v_sql := 'SELECT * FROM Table1 WHERE ID = '||V_REC_DBLINK_TABLE1.ID;
EXECUTE IMMEDIATE v_sql INTO V_REC_DBLINK_TABLE1_EXISTS;
UPDATE TABLE1
SET ID = V_REC_DBLINK_Table1.ID,
NAME = V_REC_DBLINK_Table1.NAME,
DESCRIPTION = V_REC_DBLINK_Table1.DESCRIPTION
NOTE = V_REC_DBLINK_Table1.NOTE
NUMBER = V_REC_DBLINK_Table1.NUMBER
ADDRESS = V_REC_DBLINK_Table1.ADDRESS
WHERE ID = V_REC_DBLINK_TABLE1.ID;
EXCEPTION
when no_data_found then
INSERT INTO TABLE1
(ID, NAME, DESCRIPTION, NOTE, NUMBER, ADDRESS)
Values(V_REC_DBLINK_TABLE1.ID
,V_REC_DBLINK_TABLE1.NAME
,V_REC_DBLINK_TABLE1.DESCRIPTION
,V_REC_DBLINK_TABLE1.NOTE
,V_REC_DBLINK_TABLE1.NUMBER
,V_REC_DBLINK_TABLE1.ADDRESS);
END;
BEGIN
v_sql := 'SELECT * FROM Table2 WHERE ID = '||V_REC_DBLINK_TABLE2.ID;
EXECUTE IMMEDIATE v_sql INTO V_REC_DBLINK_TABLE2_EXISTS;
UPDATE TABLE2
SET ID = V_REC_DBLINK_Table2.ID,
NAME = V_REC_DBLINK_Table2.NAME,
DESCRIPTION = V_REC_DBLINK_Table2.DESCRIPTION
WHERE ID = V_REC_DBLINK_TABLE2.ID;
EXCEPTION
when no_data_found then
INSERT INTO TABLE2
(ID, NAME, DESCRIPTION)
Values(V_REC_DBLINK_TABLE2.ID
,V_REC_DBLINK_TABLE2.NAME
,V_REC_DBLINK_TABLE2.DESCRIPTION);
END;
(and so on for all Tables via DBLINK)
Any suggestions? I just want to do that in a shorter way(maybe via Collection and loop).
I'm trying to do an upsert using merge and I'm getting an error message: Oracle - ORA-38101: Invalid column in the INSERT VALUES Clause on Merge: "MY_TABLE"."MYCOL2", even thought the table and column names indicated in the error are correct.
declare
var1 varchar2(50) := 'var1';
var2 varchar2(50) := 'var2';
procedure ins
(mycol1 IN VARCHAR2,
mycol2 IN VARCHAR2)
is
BEGIN
LOOP
BEGIN
MERGE INTO my_table USING dual ON
( MYCOL1 = mycol1
AND MYCOL2 = mycol2
)
WHEN MATCHED THEN UPDATE SET
MYCOL1 = mycol1,
MYCOL2 = mycol2
WHEN NOT MATCHED THEN INSERT
(MYCOL1, MYCOL2)
VALUES ( mycol1, mycol2 );
EXIT; -- success? -> exit loop
EXCEPTION
WHEN NO_DATA_FOUND THEN -- the entry was concurrently deleted
NULL; -- exception? -> no op, i.e. continue looping
WHEN DUP_VAL_ON_INDEX THEN -- an entry was concurrently inserted
NULL; -- exception? -> no op, i.e. continue looping
END;
END LOOP;
END;
begin
ins (var1, var2);
end;
Dont use the variable names which are same as column name of the table.
Your MATCHED clause is doing nothing.
in USING clause, there must be some query as mentioned in the comment.
I am making a function trigger that will be executed before insert new record in some table, this function trigger will insert the new record in another table with some values of the first insert. I need to execute before insert because I need the last record in the first table to compare with some fields of the new record.
The problem here is, that I can find how to get the Id of the record that will be inserted cuz the record doesn't exist yet.
I am thinking user a sequence to get the next id that will be generated, but I don't know if these cause problems if there are more than one user writing to the database at the same time.
This is my function PostgreSQL function:
CREATE FUNCTION mydb.mytriggername()
RETURNS trigger
LANGUAGE 'plpgsql'
COST 100.0
VOLATILE NOT LEAKPROOF
AS $BODY$
BEGIN
IF (
SELECT tvalue FROM mydb.myTable WHERE type = NEW.type AND sb = NEW.sb AND st= NEW.st order by id DESC LIMIT 1
) <> NEW.tvalue THEN
INSERT INTO mydb.anotherTable( type, date, desc, tp, sb, st, ref)
SELECT 'tc', NEW.date, 'newdesc', NEW.type, NEW.sb, NEW.st, CONCAT('{"id_ref":', NEW.id, '}');
END IF;
RETURN NEW;
END;
Notice that I need use it on CONCAT('{"id_ref":', NEW.id, '}'); but as I said before, I don't have the id because the new records it doesn't insert yet.
Do you have any approach to solve my problem?
you don't reveal your trigger code, so I'm not sure this is the case, but below is an example of using NEW before INSERT
t=# create table ta1(i int);
CREATE TABLE
t=# create table ta2(i int);
CREATE TABLE
t=# create function taf() returns trigger volatile not leakproof as $$
begin
if NEW.i > 0 then insert into ta2 values (NEW.i); end if;
return NEW;
end;
$$ language plpgsql
;
CREATE FUNCTION
t=# create trigger tg before insert on ta1 for each row execute procedure taf();
CREATE TRIGGER
t=# select * from ta2;
i
---
(0 rows)
t=# begin; insert into ta1 select 5;
BEGIN
INSERT 0 1
t=# select * from ta2;
i
---
5
(1 row)
t=# end;
COMMIT
I have a case when using instead-of-insert trigger is necessary. My colleagues and I wonder which one is more effective (memory usage, time to run, etc.).
The trigger checks whether the record exists in table, if no inserts the new row, otherwise updates existing row by its key. The primary key in this example is composite key of (DocumentId, VatRate).
The first variant is with checking whether the record already exists:
CREATE TRIGGER docvatsum_trg
ON DocumentVatSummary
INSTEAD OF INSERT
AS
BEGIN
IF EXISTS (
SELECT 1 FROM DocumentVatSummary a
JOIN inserted b ON (a.DocumentId = b.DocumentId AND a.VatRate = b.VatRate)
)
BEGIN
UPDATE DocumentVatSummary
SET
DocumentVatSummary.VatBase = i.VatBase,
DocumentVatSummary.VatTotal = i.VatTotal
FROM inserted i
WHERE
DocumentVatSummary.DocumentId = i.DocumentId AND
DocumentVatSummary.VatRate = i.VatRate
END
ELSE
BEGIN
INSERT INTO DocumentVatSummary
SELECT * FROM inserted
END
END;
The second variant tries to insert and if insert fails an update follows:
CREATE TRIGGER docvatsum_trg
ON DocumentVatSummary
INSTEAD OF INSERT
AS
BEGIN
SAVE TRANSACTION savepoint
BEGIN TRY
INSERT INTO DocumentVatSummary
SELECT * FROM inserted
END TRY
BEGIN CATCH
IF XACT_STATE() = 1
BEGIN
ROLLBACK TRAN savepoint
UPDATE DocumentVatSummary
SET
DocumentVatSummary.VatBase = i.VatBase,
DocumentVatSummary.VatTotal = i.VatTotal
FROM inserted i
WHERE
DocumentVatSummary.DocumentId = i.DocumentId AND
DocumentVatSummary.VatRate = i.VatRate
END
END CATCH
END;
Note: Rollback to savepoint is required, because of TRY-CATCH implementation in running transaction in TSQL.
Which one is better and why? If you have better solution, please share.
Use MERGE in your trigger as explained here:
MERGE SYNTAX
Code Example:
DECLARE #SummaryOfChanges TABLE(Change VARCHAR(20));
MERGE INTO Sales.SalesReason AS Target
USING (VALUES ('Recommendation','Other'),
('Review', 'Marketing'),
('Internet', 'Promotion'))
AS Source (NewName, NewReasonType)
ON Target.Name = Source.NewName
WHEN MATCHED THEN
UPDATE SET ReasonType = Source.NewReasonType
WHEN NOT MATCHED BY TARGET THEN
INSERT (Name, ReasonType) VALUES (NewName, NewReasonType)
OUTPUT $action INTO #SummaryOfChanges;
is there a way to fix this error or there is something i am missing in my code,im learning postgresql and i have been working on table inheritance and triggers i have two tables the temporary_object table and the persons table the persons table inherits all the properties the the temporary object has,then i have created a trigger function on the persons table which checks the records with the same id before updating the tables my problem comes when i try to run the insert query,im getting these errors
ERROR: end of trigger procedure achieved without RETURN
CONTEXT: function PL / pgSQL muli_function ()
ERROR: end of trigger procedure achieved without RETURN
SQL-state: 2F005
Context: The PL / pgSQL muli_function ()
this is by insert query
INSERT INTO persons (id, time_create, time_dead, First_name, Last_name) values (1, '2011-10-07 15:25:00 EDT', '2011-10-07 3:25 PM EDT', 'sathiya', 'james');
and this my trigger function and trigger itself
CREATE FUNCTION muli_function() RETURNS trigger AS '
BEGIN
IF tg_op = ''UPDATE'' THEN
UPDATE persons
SET time_dead = NEW.time_create
Where
id = NEW.id
AND time_dead IS NULL
;
RETURN new;
END IF;
END
' LANGUAGE plpgsql;
CREATE TRIGGER sofgr BEFORE INSERT OR DELETE OR UPDATE
ON persons FOR each ROW
EXECUTE PROCEDURE muli_function();
and these are my two tables query
CREATE TABLE temporary_object
(
id integer NOT NULL,
time_create timestamp without time zone NOT NULL,
time_dead timestamp without time zone,
PRIMARY KEY (id, time_create)
);
CREATE TABLE persons
(
First_Name text,
Last_Name text
)
INHERITS (temporary_object);
Try this:
CREATE FUNCTION muli_function() RETURNS trigger AS '
BEGIN
IF tg_op = ''UPDATE'' THEN
UPDATE persons
SET time_dead = NEW.time_create
Where
id = NEW.id
AND time_dead IS NULL
;
END IF;
RETURN new;
END
' LANGUAGE plpgsql;
UPD Better something like this:
CREATE FUNCTION muli_function() RETURNS trigger AS '
BEGIN
IF tg_op = ''UPDATE'' THEN
IF NEW.time_dead IS NULL THEN
NEW.time_dead = NEW.time_create
END IF;
END IF;
RETURN new;
END
' LANGUAGE plpgsql;
You define the trigger procedure for an INSERT OR UPDATE trigger, but don't provide a case for the INSERT (just as mu is too short is commented), only the UPDATE. So the trigger executes the procedure for an INSERT, then it does not find anything to execute and exits without a RETURN, which is obviously an error.