PostgreSQL, add row to table when a row is created in another table - sql

I am trying to create a trigger function, to create a new row, in a table, when a value is modified or created in another table. But the problem is that I need to insert in the other table, the primary key that provoked the trigger function.
Is there a way to do it?
Basically, when an insert or update will be done in table 1, I want to see in table 2 a new row, with one field filed with the value of the primary key of the row in table1 that provoked the trigger.
begin
INSERT INTO resultados_infocorp(id_user, Procesado)
VALUES (<PRIMARY_KEY>,false)
RETURN NEW;
End;
This is because if Procesado is false, thank to the id_user I will make some validations, but the ID of the user is necesary and I cant do it from the backend of my project, because I have many db inputs.
PD: The primary key of the new table is a sequence, this is the reason why I am not passing this arg.
CREATE TRIGGER resultados_infocorp_actualizar
AFTER INSERT OR UPDATE OF id_user, fb_id, numdocumento, numtelefono, tipolicencia, trabajoaplicativo
ON public.usuarios
FOR EACH ROW
EXECUTE PROCEDURE public.update_solicitudes_infocorp();

You have not shown the trigger definition. Still if you want the PK value then something like:
INSERT INTO resultados_infocorp(id_user, Procesado)
VALUES (NEW.pk_fld,false)
Where pk_fld is the name of your PK field. Take a look here:
https://www.postgresql.org/docs/current/plpgsql-trigger.html
for what is available to a trigger function. For the purpose of this question the important part is:
NEW
Data type RECORD; variable holding the new database row for INSERT/UPDATE operations in row-level triggers. This variable is null in statement-level triggers and for DELETE operations.

Related

Can I manually trigger before insert function for already inserted records in Postgres?

I am using PostgreSQL 14.5
I have a beneficiaries table where people are registered and I have BEFORE INSERT trigger function that generates a unique ID based on their location (if Paris - PAR-001, if London - LON-004, etc) and it works.
But the problem is, lots of records are inserted before writing this function, and since the function is triggered BEFORE INSERT I can't have the IDs for them.
I have seen the documentation and didn't find how to trigger the function for previous records
Can I manually trigger this function for all records once or any other solution?
Add an additional trigger like this:
CREATE TRIGGER temp BEFORE UPDATE ON tab
FOR EACH ROW WHEN (OLD.id IS NULL)
EXECUTE FUNCTION your_trigger_func();
The trigger function is the one you use in your BEFORE INSERT trigger. Then update all these rows:
UPDATE tab SET id = id WHERE id IS NULL;
You can drop the trigger when you are done.
You can perform the update in batches and run VACUUM on the table in between, to avoid bloat by a single massive update.

Edit inserted table sql

When insert I need edit a value if it is null. I create a trigger but I don't know how to edit inserted table.
ALTER TRIGGER [trigger1] on [dbo].[table]
instead of insert
as
declare #secuencia bigint, #ID_PERSONA VARCHAR;
select #secuencia = SECUENCIA from inserted
select #ID_PERSONA = ID_PERSONA from inserted
if #secuencia is null begin
set inserted.SECUENCIA = NEXT VALUE FOR SEQ_BIOINTEG --(Sequence)
end
i dont know how to edit inserted table.
You do not. That table is read only.
Note how your trigger also says:
instead of insert
There is no way to edit the inserted table.
What you do instead, is setting up an INSERT command for the original table, using the data from the inserted table to filter to the ROWS of inserted - mostly by a join.
Changing inserted makes no sense, logically - because triggers in SQL are one of two things:
INSTEAD OF - then there is no actual insert happening for inserted to start with. Instead of doing the insert, the trigger is called. As such, changing inserted - makes no sense.
AFTER - then the insert already happened (and you UPDATE the rows). As the trigger runs after the update, changing inserting makes no sense.
Note that I say ROWS - your trigger has one very basic error: it assumes inerted contains ONE row. It is a table - it is possible the changes come from an insert statement that inserts multiple rows (which is trivial, i.e. select into, or simply an insert with values for multiple rows). Handle those.
select #ID_PERSONA = ID_PERSONA from inserted
Makes NO sense - inserted is a table, so ID_PERSONA from inserted contains what value, if 2 rows are inserted? You must treat inserted like any other table.
Apart from all the varied issues with your trigger code, as mentioned by others, the easiest way to use a SEQUENCE value in a table is to just put it in a DEFAULT constraint:
ALTER TABLE dbo.[table]
ADD CONSTRAINT DF_table_seq
DEFAULT (NEXT VALUE FOR dbo.SEQ_BIOINTEG)
FOR SECUENCIA;

PostgreSQL: Inserting tuples in multiple tables using a view and a trigger

I am trying to build an order system that is able to insert a compound order that consists of multiple items and amounts. My database layout is as follows: I have an order table, containing an autoincrement id, item_id, amount and order_group_id columns. I also have an order_group table containing an autoincrement id and a person_id column. The idea is that when a person orders, one new order_group entry is created, and its id is used as the fk in the orders that the person has done.
I presume that this would normally be done in the code of the application. However, I am using postgrest to provide an API for me, which suggests creating a custom view to insert compound entries via that route. This is described here.
This is what I have so far:
CREATE FUNCTION kzc.new_order()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
DECLARE
group_id int;
BEGIN
INSERT INTO kzc.order_group (person) VALUES (new.person) RETURNING id AS group_id;
INSERT INTO kzc."order" (item, amount, order_group) VALUES (new.item_id, new.amount, group_id);
RETURN new;
END;
$$;
CREATE TRIGGER new_order
INSTEAD OF INSERT ON kzc.new_order
FOR EACH ROW
EXECUTE FUNCTION kzc.new_order()
However, this code makes a new ordergroup for every order that is in the compound insert. How can I make it so that my code only makes one new ordergroup entry and assigns its id to all orders?
Thanks in advance!
I suggest that you add an order_group_id column to the new_order view and create a sequence for it. Then create a DEFAULT value for the column:
ALTER VIEW kzc.new_order
ALTER order_group_id SET DEFAULT currval('order_group_id_seq');
Add a BEFORE INSERT trigger FOR EACH STATEMENT that just calls nextval for the sequence. The currval calls will all pick up the same generated value.
Then you have that number in your trigger and can use it as a primary key for order_group.
To avoid adding the row multiple times, use
INSERT INTO kzc.order_group (id, person)
VALUES (NEW.order_group_id, NEW.person)
ON CONFLICT (id) DO NOTHING;

Is it possible to change a delete to an update using triggers?

Is there any way in Firebird to execute an UPDATE instead a DELETE through a trigger?
This is possible in Microsoft SQL Server by declaring the triggers as "INSTEAD".
The problem is that we have an application that uses a Firebird database and we want to prevent the deletes of records and mark then as "deleted" (a new field), but without showing any error to the user, and "cheating" the app.
You cannot do this with tables, but you can do it with views. Views can have triggers on insert, update and delete that modify the underlying table(s). See also Updatable Views in the Firebird 2.5 Language Reference.
In short, create a table for the data, add a view, add triggers that insert/update/delete through the view to the underlying table. Users can then use the view as if it is a table.
An example
I'm using Firebird 3, but this will work with minor modifications in Firebird 2.5 and earlier.
A table example_base:
create table example_base (
id bigint generated by default as identity constraint pk_example_base primary key,
value1 varchar(100),
deleted boolean not null default false
)
A view example:
create view example (id, value1)
as
select id, value1
from example_base
where not deleted;
Do not create the view with with check option, as this will disallow inserts as the absence of the deleted column in the view will prevent Firebird from checking the invariant.
Then add an insert trigger:
create trigger tr_example_insert before insert on example
as
begin
if (new.id is not null) then
-- Don't use identity
insert into example_base(id, value1) values (new.id, new.value1);
else
-- Use identity
-- mapping generated id to new context
-- this way it is available for clients using insert .. returning
insert into example_base(value1) values (new.value1)
returning id into :new.id;
end
The above trigger ensures the 'by default as identity' primary key of the underlying table is preserved, and allows insert into example .. returning to report on the generated id.
An update trigger
create trigger tr_example_update before update on example
as
begin
-- Consider ignoring modification of the id (or raise an exception)
update example_base
set id = new.id, value1 = new.value1
where id = old.id;
end
The above trigger allows modification of the primary key; you may want to consider just ignoring such a modification or even raising an exception.
And finally a delete trigger:
create trigger tr_example_delete before delete on example
as
begin
update example_base
set deleted = true
where id = old.id;
end
This trigger will mark the record in the base table as deleted.
To use this, just grant your users select, insert and update privileges to the view (and not the table).
The only caveat I'm aware of is that defining foreign keys will need to point to example_base, not to example, and the behavior of foreign keys will be slightly off. The record in the base table will continue to exist, so the foreign key will not block deletion. If that is something that is necessary, you will need to emulate constraint behavior (which could be tricky).
YES! It can be made on VIEWs.
That's the way I solved it.
If a View has a trigger, then the trigger is the responsible of making the real update or delete on the underlying table.... So... a DELETE trigger that makes an UPDATE to the table solved my problem.

Using Trigger to insert record in another table, AND write back the ID into original record ..?

I have a simple trigger setup, which is used to insert records into a RentJournal table, whenever there is a record inserted in the UnitAGA table.
The RentJournal table has a primary key ID column named RentJournalID, which is auto incrementing. The UnitAGA table also has a nullable foreign key column named RentJournalID, which links each UnitAGA entry, to its corresponding entry in RentJournal table (which is inserted through the Trigger below).
Problem is that currently this Trigger is only inserting values into RentJournal table. But now I want to also fetch the ID assigned for each RentJournal entry through this Trigger, and write that into the corresponding UnitAGA record, whose insert actually triggered the Trigger in the first place. How do I do this ?
The Trigger code as of right now is this:
USE [RentDB]
GO
ALTER TRIGGER [RTS].[InsertRentJournalEntry]
ON [RTS].[UnitAGA]
AFTER INSERT
AS
BEGIN
INSERT INTO RTS.RentJournal
(UnitId, AdjustmentType, EffectiveDate, ReferenceFormNo)
SELECT
UnitId, 'AGA', EffectiveDate, ReferenceFormNo FROM inserted
END
Have a look at the INSERT logical table that is available in insert triggers:
DML triggers use the deleted and inserted logical (conceptual) tables. They are structurally similar to the table on which the trigger is defined, that is, the table on which the user action is tried. The deleted and inserted tables hold the old values or new values of the rows that may be changed by the user action. For example, to retrieve all values in the deleted table, use: SELECT * FROM deleted
http://technet.microsoft.com/en-us/library/ms189799.aspx
Then use ##IDENTITY to get the value of the identity column on your RentJournal table.
So you should be able to do something like:
update INSERTED set RentJournalID = ##IDENTITY