Skip insert query if value is null - sql

I have a postgreSQL trigger for my social media project where once a comment is inserted, the trigger inserts a new row into the commentData table and the subscriptions table as shown below.
create or replace function public.initialize_comment_data_subscribe()
returns trigger as $$
begin
INSERT INTO "commentData" (comment_id, post_id, vote_count, children_count)
VALUES (new.id, new.post_id, 0, 0);
INSERT INTO subscriptions (comment_id, post_id, user_id, email)
VALUES (new.id, new.post_id, new.author_id, (SELECT email from users WHERE user_id = new.author_id));
RETURN NEW;
end;
$$ language plpgsql;
create trigger on_create_comment
after INSERT on comments
for each row execute procedure public.initialize_comment_data_subscribe();
When the email is null in the users table for a specific user_id, the whole thing fails due to the following line
... (SELECT email from users WHERE user_id = new.author_id));
But instead I would like the first insert to continue working and the second one to be skipped if the email is null.
How do I go about doing this? Or is it better to split the insert queries and have 2 triggers instead.

Do a SELECT INSERT instead:
INSERT INTO subscriptions (comment_id, post_id, user_id, email)
SELECT new.id, new.post_id, new.author_id, email
from users WHERE user_id = new.author_id AND email is not null

Related

Postgres Multiple WITH blocks to do multiple inserts and updates in one query

My database has 3 tables, users, firms, and stashes. The stashes belong to users and the users belong to firms. The users also have a foreign key for currently active stash.
I want to run a query when a new user registers to create a new firm for them, create a new user, create a new stash, and also set that stash to be active. Here is what I have and it runs but it is not setting the active stash parameter on the new user correctly.
I would love some help and also just general advice if the way that I have structured this query is not the most efficient. Thank you
WITH insert1 AS (
INSERT INTO firms (name) VALUES ('Jack_testFirm') RETURNING id AS ret_firm_id
)
,
insert2 AS (
INSERT INTO users (email, admin, firm_id)
SELECT 'jack#gmail.com', TRUE, ret_firm_id
FROM insert1
RETURNING users.id AS new_user_id
)
,
insert3 AS (
INSERT INTO stashes (name, user_id)
SELECT 'Stash 1', new_user_id FROM insert2
RETURNING stashes.id AS new_stash_id
)
UPDATE users
SET active_stash = (SELECT new_stash_id FROM insert3)
WHERE users.id = (SELECT new_user_id FROM insert2)
After the query, I should have a brand new user, 'jack#gmail.com' which belongs to the firm 'Jack_testFirm', a new stash which belongs to 'jack#gmail.com' and jack#gmail.com should store that stashes id as the active stash in the users table.
Use procedure. Idea is first materialize all of your insert operation, use variable hold your new_stash_id and new_user_id, then do the update.
Procedure all happen in a single transaction (Do stored procedures run in database transaction in Postgres?), all fail or all success.
dbfiddle
Call procedure: call test()
Code:
CREATE OR REPLACE PROCEDURE test ()
LANGUAGE plpgsql
AS $$
DECLARE
_new_stash_id bigint;
_new_user_id bigint;
BEGIN
WITH insert1 AS (
INSERT INTO firms (name)
VALUES ('Jack_testFirm')
RETURNING
firm_id AS ret_firm_id
), insert2 AS (
INSERT INTO users (email, admin, firm_id)
SELECT
'jack#gmail.com',
TRUE,
ret_firm_id
FROM
insert1
RETURNING
users.user_id AS new_user_id
),
insert3 AS (
INSERT INTO stashes (name, user_id)
SELECT
'Stash 1',
new_user_id
FROM
insert2
RETURNING
new_stash_id,
user_id
)
SELECT
new_stash_id,
user_id
FROM
insert3 INTO _new_stash_id,
_new_user_id;
RAISE NOTICE '_new_stash_id: %', _new_stash_id;
RAISE NOTICE '_new_user_id: %', _new_user_id;
UPDATE
users
SET
active_stash = _new_stash_id
WHERE
user_id = _new_user_id;
END
$$;

Trigger for conditional insert into table

I have subscription data that is being appended to a table in real-time (via kafka). i have set up a trigger such that once the data is added it is checked for consistency. If checks pass some of the data should be added to other tables (that have master information on the customer profile etc.). The checks function i wrote works fine but i keep getting errors on the function used in the trigger. The function for the trigger is:
CREATE OR REPLACE FUNCTION update_tables()
RETURNS TRIGGER
LANGUAGE plpgsql
AS
$$
BEGIN
CASE (SELECT check_incoming_data()) WHEN 0
THEN INSERT INTO
sub_master(sub_id, sub_date, customer_id, product_id)
VALUES(
(SELECT sub_id::int FROM sub_realtime WHERE CTID = (SELECT MAX(CTID) FROM sub_realtime)),
(SELECT sub_date::date FROM sub_realtime WHERE CTID = (SELECT MAX(CTID) FROM sub_realtime)),
(SELECT customer_id::int FROM sub_realtime WHERE CTID = (SELECT MAX(CTID) FROM sub_realtime)),
(SELECT product_id::int FROM sub_realtime WHERE CTID = (SELECT MAX(CTID) FROM sub_realtime))
);
RETURN sub_master;
END CASE;
RETURN sub_master;
END;
$$
The trigger is then:
CREATE TRIGGER incoming_data
AFTER INSERT
ON claims_realtime_3
FOR EACH ROW
EXECUTE PROCEDURE update_tables();
What I am saying is 'if checks pass then select data from the last added row and add them to the master table'. What is the best way to structure this query?
Thanks a lot!
The trigger functions are executed for each row and you must use a record type variable called "NEW" which is automatically created by the database in the trigger functions. "NEW" gets only inserted records. For example, I want to insert data to users_log table when inserting records to users table.
CREATE OR REPLACE FUNCTION users_insert()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
begin
insert into users_log
(
username,
first_name,
last_name
)
select
new.username,
new.first_name,
new.last_name;
return new;
END;
$function$;
create trigger store_data_to_history_insert
before insert
on users for each row execute function users_insert();

PostgreSQL insert triggers and many-to-many tables

I have two tables in a postgres database articles and users. These two tables are connected through a many-to-many relation, so there's a third table called articles_users that contains foreign key relations to both articles and users.
What I'd like to do, is create a trigger that runs when a new entry is inserted into the articles table and connects that article to each user in my database through the articles_users table. Is this possible with triggers and if so, how should I write it?
I have a rough understanding of how triggers work and have some pseudosql that looks like this:
create function public.connect_articles_to_users()
returns trigger as $$
begin
-- Here's some pseudocode to describe what I'd like to happen
-- Not sure if temprow is the right structure here
for temprow in
select * from users
loop
insert into articles_users (id, users.id) values (new.id, users.id)
end loop
return new
end;
$$ language plpgsql security definer;
-- trigger the function every time a and article is created
create trigger on_article_created
after insert on articles
for each row execute procedure public.connect_articles_to_users();
Your trigger is already executes the function for each row of inserted article and you don't need a loop for users, you can insert many rows in one insert:
CREATE OR REPLACE FUNCTION connect_articles_to_users()
RETURNS trigger AS
$$
BEGIN
INSERT INTO articles_users (article_id, user_id)
SELECT NEW.id, id FROM users
;
RETURN NEW;
END;
$$
LANGUAGE 'plpgsql';
Provided the articles_users table has columns article_id and user_id insert a row for every user
insert into articles_users (article_id, user_id)
select new.id, users.id
from users;

SQL - How to create a trigger for two joined tables which is used for inserting

Ok , so I know that inserting information in a view based on two joined tables is impossible.
In order to do so , I need to create a trigger to insert the information in both tables , when an insert is made in that view.
For example :
CREATE VIEW myJoinedView AS
SELECT name,g.value from students
JOIN grades g on g.id=students.id;
The trigger is not working :
CREATE TRIGGER myTrigger
INSTEAD OF INSERT ON myJoinedView
BEGIN
INSERT INTO students
(name,value)
SELECT i.myJoinedView
FROM inserted i
INNER JOIN grades
ON i.id = grades.id
END myTrigger;
Then I'm trying to insert :
INSERT INTO myJoinedView VALUES ('Alex',10);
I don't know if the syntax is correct , I did not find any helpful documentation on this specific type of trigger.
I'm getting this error:
Error(10,46): PLS-00103: Encountered the symbol "end-of-file" when
expecting one of the following: ( begin case declare end exception
exit for goto if loop mod null pragma raise return select update
while with
<< continue close current delete fetch lock
insert open rollback savepoint set sql execute commit forall merge
pipe purge
Any help will be well received.
Thank you!
You need to either perform the inserts separately with separate single table insert or merge statements or by using a multi table insert (insert all) statement. Assuming you have a sequence to generate the id you are joining on for example this code will work in a very rudimentary way, but has some significant issues:
create table students ( id number primary key
, name varchar2(60));
create table grades( id number not null
, value number
, constraint grades_fk1 foreign key (id) references students(id));
create sequence student_id_seq;
create or replace view studentgrades as
select name, value from students s join grades g on s.id = g.id;
create or replace trigger studentgrades_ii_trg
instead of insert on studentgrades
begin
insert all into students(id, name) values (student_id_seq.nextval, name)
into grades(id, value) values (student_id_seq.nextval, value)
select :new.name name, :new.value value from dual;
end;
/
insert into studentgrades values ('Alex',10);
insert into studentgrades values ('Alex',8);
The BIG issue with the above trigger is that every time a grade is inserted for 'Alex' a new student record for 'Alex' is also created instead of reusing the previous student record for 'Alex'. That's probably not the desired behavior. Instead it should probably just insert a new grade record for Alex. One way to acheive this is for the studentgrades view to include the id column from the students table so you can uniquely identify which student to add the grade to, updating the trigger as needed:
create or replace view studentgrades as
select s.id, name, value from students s join grades g on s.id = g.id;
create or replace trigger studentgrades_ii_trg
instead of insert on studentgrades
declare
newid students.id%type;
begin
if :new.id is null then
newid := student_id_seq.nextval;
else
newid := :new.id;
end if;
insert all when :new.id is null
then into students(id, name) values (id, name)
else into grades(id, value) values (id, value)
select newid id, :new.name name, :new.value value from dual;
end;
/
insert into studentgrades values (null, 'Paul',10);
insert into studentgrades values (student_id_seq.currval, 'Paul',8);
However, now what happens if you try this:
insert into studentgrades values (student_id_seq.currval, 'Mary',10);
In this case the name is effectively ignored and Paul gets a new grade so again this isn't quite right. The question is should Paul's name be updated to Mary, or should a new student record for Mary be created, or should an exception be raised?

Creating a trigger with a case statement

I have these two tables:
USERS(username, role_id)
COMMISSION_RATES(username, commission_rate)
users.username is the primary key, commission_rates.username is a foreign key.
I want to write a trigger, after an insert on users, check if role_id = 2, then insert into commission_rates the users.username, and 0 for commission rate.
This is what I have so far, it doesn't work though:
create or replace TRIGGER SETCOMISSIONRATE
AFTER INSERT ON USERS
BEGIN
CASE
WHEN users.role_id = 2 THEN
INSERT INTO COMISSION_RATE
(username,
comission_rate)
VALUES (
:NEW.username,
0)
END;
Any help would be appreciated
It should go like this:
CREATE OR REPLACE TRIGGER SETCOMISSIONRATE
AFTER INSERT ON USERS FOR EACH ROW
WHEN (new.role_id = 2)
BEGIN
INSERT INTO COMISSION_RATE (username, comission_rate)
VALUES (:new.username, 0);
END;
WHEN condition must be in the definition part, not in the body. They can only by used for row trigger.
create or replace
TRIGGER SETCOMISSIONRATE
AFTER INSERT ON USERS
FOR EACH ROW
WHEN (NEW.role_id = 2)
BEGIN
INSERT INTO COMISSION_RATE
(username,
comission_rate)
VALUES (
:NEW.username,
0)
END;