SQLite before insert trigger - sql

I'm trying to insert duplicates data to table B when PKA violated using a BEFORE INSERT TRIGGER as the following example:
CREATE TABLE A( Col INTEGER, Coll TEXT(25), CONSTRAINT PKA PRIMARY KEY(Col, Coll) ON CONFLICT IGNORE);
CREATE UNIQUE INDEX IX_A ON A(Col, Coll);
CREATE TABLE B( Col INTEGER, Coll TEXT(25));
CREATE INDEX IX_B ON B(Col, Coll);
CREATE TRIGGER Trig
BEFORE INSERT
ON A
WHEN (Col = New.Col AND Coll = New.Coll)
BEGIN
INSERT INTO B(Col, Coll) VALUES(New.Col, New.Coll);
END;
But, it seems like the column Col is not accessible there, so it throws:
no such column: Col
Even when I change the conditions to
New.Col IN(SELECT Col FROM A)
AND
New.Coll IN(SELECT Coll FROM A)
I get another error message:
UNIQUE constraint failed: A.Col, A.Coll
While it shouldn't because of ON CONFLICT IGNORE.
Why did I get those error messages? (What's the cause).
How can I use the trigger to insert duplicates into another table?

You don't need the index:
CREATE UNIQUE INDEX IX_A ON A(Col, Coll);
because you have already defined (Col, Coll) as the PRIMARY KEY and furthermore, with this index, although you have ON CONFLICT IGNORE defined for a duplicate row, you will receive an error if you try to insert a duplicate row, because ON CONFLICT IGNORE is not defined for the index.
So drop it:
DROP INDEX IF EXISTS IX_A;
Now, change the code of the trigger to this:
CREATE TRIGGER Trig
BEFORE INSERT
ON A
WHEN EXISTS (SELECT 1 FROM A WHERE Col = New.Col AND Coll = New.Coll)
BEGIN
INSERT INTO B(Col, Coll) VALUES(New.Col, New.Coll);
END;
EXISTS checks the table A if it already contains a row with column values the same as the ones to be inserted and if it does then the new row is inserted in table B.
You could also write the trigger like this:
CREATE TRIGGER Trig
BEFORE INSERT
ON A
BEGIN
INSERT INTO B(Col, Coll)
SELECT Col, Coll FROM A
WHERE (Col, Coll) = (New.Col, New.Coll);
END;

Related

SELECT on same table AFTER INSERT TRIGGER fails?

I am writing a small AFTER INSERT trigger being executed after insert of a row in a table A.
So simplified we´ve got these three tables:
CREATE TABLE A (Id INTEGER, datum DATE, ...);
CREATE TABLE B (Id INTEGER, ...);
CREATE TABLE C (Id INTEGER, Aref INTEGER, Bref INTEGER, FOREIGN KEY(Aref) REFERENCES A(Id), FOREIGN KEY(Bref) REFERENCES B(Id));
So now the tricky thing is, that the rows out of table B should only be selected, if they are EITHER not referenced at all in table C OR, if they are referenced in table C, the datum field in the row of A also being referenced in this row of C IS NOT NULL.
You´re getting it?
So I tried my luck with a select clause looking the following:
SELECT * FROM B b WHERE NOT EXISTS(SELECT c.* FROM C c, A a WHERE c.Bref = b.Id AND c.Aref = A.Id and a.datum IS NULL);
So now the trigger is throwing an exception, because "the table is modified at the moment and the trigger could probably not see it" cited the error message. But I need the information of table A and I need it to be an AFTER INSERT Trigger cause the same trigger is using this rows for a calculation itself inserting rows in a table D with reference to this row of A.
So now the question is, how is it possible to select this rows of A after insert and make sure it keeps consistent?

How to automatically update second table with some same information after insert into first table

When I enter a new record in one table, I need to have some of the information from the first table be automatically added to the second table. I unsuccessfully tried triggers to do this.
My primary table looks like this:
CREATE TABLE demographics (
person_local_id BIGSERIAL UNIQUE PRIMARY KEY,
first_name VARCHAR(50)...[other lines]
);
I set up the child table like this:
CREATE TABLE pedigree (
pedigree_id BIGSERIAL PRIMARY KEY NOT NULL,
person_local_id BIGSERIAL NOT NULL,
person_sex VARCHAR(10),
father VARCHAR(10) DEFAULT 0,
mother VARCHAR(10) DEFAULT 0,
FOREIGN KEY (person_local_id) REFERENCES demographics (person_local_id)
ON DELETE CASCADE ON UPDATE CASCADE
);
My approach was to create a trigger on the demographics primary table such that any time a record was added to it, a corresponding record would be added to the pedigree table consisting of just the person_local_id. I added a foreign key on the pedigree table that referenced the column in the demographics that I need to copy over to the pedigree table in that column.
Then I created a trigger, but it doesn't work. I tried this with and without the word "EXECUTE".
CREATE TRIGGER into_pedigree AFTER INSERT OR UPDATE ON demographics
FOR EACH ROW EXECUTE INSERT INTO pedigree (person_local_id) SELECT (NEW.person_local_id) FROM NEW;
I keep getting syntax errors but I can't identify the error:
ERROR: syntax error at or near "INSERT"
LINE 2: FOR EACH ROW EXECUTE INSERT INTO pedigree (person_local_id) ...
^
I also tried this, adding the name:
CREATE TRIGGER into_pedigree ON identify_relatives_database.demographics
AFTER INSERT
AS
BEGIN
INSERT INTO pedigree (person_local_id) VALUES (INSERTED.person_local_id)
END;
But I get the error message:
ERROR: syntax error at or near "ON"
LINE 1: CREATE TRIGGER into_pedigree ON demographics
^
I appreciate your assistance.
You may try this.
CREATE TRIGGER [dbo].[Customer_INSERT]
ON [dbo].[demographics]
AFTER INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE #CustomerId INT
SELECT #CustomerId = INSERTED.person_local_id FROM INSERTED
IF EXISTS ( SELECT * FROM PEDIGREE WHERE person_local_id = #CustomerId)
BEGIN
--- here col is the required column name need to be modified,
--- since you are inserting person_local_id from base table which is auto generated and not suppose to be change in any condition
UPDATE PEDIGREE SET COL = INSERTED.COL WHERE person_local_id = #CustomerId
END
ELSE
BEGIN
INSERT INTO pedigree (person_local_id)
VALUES(#CustomerId)
END
END
Although I don't find anything related to your update part. Since you are inserting primary key from base table as foreign key in child table, so for normalization it is not going to changed in any condition. So i don't think you need update part in your trigger Hence your required trigger will be:
CREATE TRIGGER [dbo].[Customer_INSERT]
ON [dbo].[demographics]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO pedigree (person_local_id)
SELECT INSERTED.person_local_id FROM INSERTED
END
You should try this :
CREATE TRIGGER into_pedigree ON demographics
FOR INSERT
AS
insert into pedigree (person_local_id)
values(inserted.person_local_id);
PRINT 'AFTER INSERT trigger fired.'
GO

How to insert a newly generated id into another table with a trigger in postgresql?

Basically, users when they create a new record in mytable1, there is an id field that needs to be the same across multiple tables. I achieve this by having mytable2 with the s_id as primary key
My current function looks like
CREATE OR REPLACE FUNCTION test.new_record()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
BEGIN
case when new.s_id in (select s_id from mytable1) then
insert into mytable2 (sprn, date_created) select max(s_id) +1, now() from mytable2 ;
update mytable1 set new.s_id = (select max(b.s_id) from mytable2 b);
end case;
RETURN new;
END;
$function$;
Intended was when the s_id is replicated then it would create a new entry on mytable2. This new entry would then be updated onto mytable1
Problem with this function is that right now it does not recognise the new on the update part of the function.
How to keep the s_id take the value on every new insert ?
If you want to have one "generator" across multiple tables, create one sequence that is used across all those tables for the default value:
create sequence the_id_sequence;
create table one
(
id integer primary key default nextval('the_id_sequence')
.... other columns
);
create table two
(
id integer primary key default nextval('the_id_sequence')
.... other columns ...
);
If you want to replicate an ID from one table to another during insert, you only need one sequence:
create table one
(
-- using identity is the preferred over "serial" to auto-generate PK values
id integer primary key generated always as identity
);
create table two
(
id integer primary key
);
create or replace function insert_two()
returns trigger
as
$$
begin
insert into two (id) values (new.id);
return new;
end;
$$
language plpgsql;
create trigger replicate_id
before insert on one
for each row
execute procedure insert_two();
Then if you run:
insert into one (id) values (default);
A row with exactly the same id value will be inserted into table two.
If you don't have a generated ID column so far, use the following syntax:
alter table one
add testidcolumn bigint generated always as identity;

Insert inserted id to another table

Here's the scenario:
create table a (
id serial primary key,
val text
);
create table b (
id serial primary key,
a_id integer references a(id)
);
create rule a_inserted as on insert to a do also insert into b (a_id) values (new.id);
I'm trying to create a record in b referencing to a on insertion to a table. But what I get is that new.id is null, as it's automatically generated from a sequence. I also tried a trigger AFTER insert FOR EACH ROW, but result was the same. Any way to work this out?
To keep it simple, you could also just use a data-modifying CTE (and no trigger or rule):
WITH ins_a AS (
INSERT INTO a(val)
VALUES ('foo')
RETURNING a_id
)
INSERT INTO b(a_id)
SELECT a_id
FROM ins_a
RETURNING b.*; -- last line optional if you need the values in return
Related answer with more details:
PostgreSQL multi INSERT...RETURNING with multiple columns
Or you can work with currval() and lastval():
How to get the serial id just after inserting a row?
Reference value of serial column in another column during same INSERT
Avoid rules, as they'll come back to bite you.
Use an after trigger on table a that runs for each row. It should look something like this (untested):
create function a_ins() returns trigger as $$
begin
insert into b (a_id) values (new.id);
return null;
end;
$$ language plpgsql;
create trigger a_ins after insert on a
for each row execute procedure a_ins();
Don't use triggers or other database Kung fu. This situation happens every moment somewhere in the world - there is a simple solution:
After the insertion, use the LASTVAL() function, which returns the value of the last sequence that was auto-incremented.
Your code would look like:
insert into a (val) values ('foo');
insert into b (a_id, val) values (lastval(), 'bar');
Easy to read, maintain and understand.

Updating foreign keys while inserting into new table

I have table A(id).
I need to
create table B(id)
add a foreign key to table A that references to B.id
for every row in A, insert a row in B and update A.b_id with the newly inserted row in B
Is it possible to do it without adding a temporary column in B that refers to A? The below does work, but I'd rather not have to make a temporary column.
alter table B add column ref_id integer references(A.id);
insert into B (ref_id) select id from A;
update A set b_id = B.id from B where B.ref_id = A.id;
alter table B drop column ref_id;
Assuming that:
1) you're using postgresql 9.1
2) B.id is a serial (so actually an int with a default value of nextval('b_id_seq')
3) when inserting to B, you actually add other fields from A otherwise the insert is useless
...I think something like this would work:
with n as (select nextval('b_id_seq') as newbid,a.id as a_id from a),
l as (insert into b(id) select newbid from n returning id as b_id)
update a set b_id=l.b_id from l,n where a.id=n.a_id and l.b_id=n.newbid;
Add the future foreign key column, but without the constraint itself:
ALTER TABLE A ADD b_id integer;
Fill the new column with values:
WITH cte AS (
SELECT
id
ROW_NUMBER() OVER (ORDER BY id) AS b_ref
FROM A
)
UPDATE A
SET b_id = cte.b_ref
FROM cte
WHERE A.id = cte.id;
Create the other table:
CREATE TABLE B (
id integer CONSTRAINT PK_B PRIMARY KEY
);
Add rows to the new table using the referencing column of the existing one:
INSERT INTO B (id)
SELECT b_id
FROM A;
Add the FOREIGN KEY constraint:
ALTER TABLE A
ADD CONSTRAINT FK_A_B FOREIGN KEY (b_id) REFERENCES B (id);
PostgeSQL dialect.
You might use an anonymous code block like this
do $$
declare
category_cursor cursor for select id from schema1.categories;
r_category bigint;
setting_id bigint;
begin
open category_cursor;
loop fetch category_cursor into r_category;
exit when not found;
insert into schema2.setting(field)
values ('field_value') returning id into setting_id;
update schema1.categories set category_setting_id = setting_id
where category_id = r_category;
end loop;
end; $$
Let assume we have two tables first - categories, second - settings which must be applied to these categories.
First step - declare cursor(collect ids from categories), and variabels where we store temporary data
Loop cursor inserting values 'field_value' into settings
Store id in variable setting_id
Update table categories with setting_id