Postgres insert trigger to update multiple columns on new row if null - sql

If an entire row of null values is inserted into my table, I am wanting to execute a trigger to change all columns the newly inserted row to the average instead of null.
I have created the trigger function:
create or replace function teammate_null_check()
returns trigger as
$$
begin
if new.score is null then new.score = (select avg(score) from doubles.teammate);
elsif new.goals is null then new.goals = (select avg(goals) from doubles.teammate);
elsif new.assists is null then new.assists = (select avg(assists) from doubles.teammate);
elsif new.saves is null then new.saves = (select avg(saves) from doubles.teammate);
elsif new.shots is null then new.shots = (select avg(shots) from doubles.teammate);
end if;
return new;
end;
$$ language plpgsql
And the trigger event:
create trigger teammate_trigger
before insert on doubles.teammate
for each row
execute procedure teammate_null_check()
However when I insert a null value for all columns on my table, the trigger only sets the first column (score) to the average. I've tried using ifelse and a case statement and they both only update the first column.
This is what the table looks like after insert:
score
goals
assist
saves
shots
1234
1
2
3
4
1234
null
null
null
null
How can I update all columns if the entire row is inserted null?

ELsif for the alghorithm to choose only one column, but if ypu want all columns t be checked, you need to check eac column individually
create or replace function teammate_null_check()
returns trigger as
$$
begin
if new.score is null then
new.score = (select avg(score) from doubles.teammate);
end if;
if new.goals is null then new.goals = (select avg(goals) from doubles.teammate); end if;
if new.assists is null then new.assists = (select avg(assists) from doubles.teammate); end if;
if new.saves is null then new.saves = (select avg(saves) from doubles.teammate); end if;
if new.shots is null then new.shots = (select avg(shots) from doubles.teammate);
end if;
return new;
end;
$$ language plpgsql

Related

Trigger to update age when inserted age < 0

I am trying to set age = 0 if the age is inserted as negative. I don't know what is wrong with the code:
CREATE TRIGGER verify_age
BEFORE INSERT ON customers
FOR EACH ROW
WHEN (NEW.age < 0)
BEGIN
UPDATE customers
SET age = 0
END;
The syntactical error in your code is that it is missing a ; before END, but even if you correct this it would not solve your problem.
You need a WHERE clause in the UPDATE statement so that you update only the new row and not the whole table.
The condition in the WHERE clause will check for the rowid of the new row, but this rowid exists only after the row is inserted.
So, you must change the trigger to an AFTER INSERT trigger:
CREATE TRIGGER verify_age AFTER INSERT ON customers
WHEN NEW.age < 0
BEGIN
UPDATE customers
SET age = 0
WHERE rowid = NEW.rowid;
END;
See the demo.
Try this:
CREATE OR REPLACE TRIGGER validate_age
BEFORE INSERT
ON customers
FOR EACH ROW
BEGIN
IF :new.age < 0
THEN
:new.age := 0;
END IF;
END;
or :
CREATE TRIGGER validate_age
BEFORE INSERT ON customers
BEGIN
SELECT
CASE
WHEN NEW.age < 0 THEN
NEW.age = 0
END;
END;

BULK COLLECT in a trigger

I need to create a trigger that updates a column in a table based on a transaction that happens in another table. Is there a way to use BULK COLLECT on joining two tables?
I need to collect data multiple times, is it possible to use BULK COLLECT inside another BULK COLLECT?
HERE is my existing trigger
create or replace trigger trans_hist_trg
AFTER INSERT OR UPDATE OF reason ON transaction_history
FOR EACH ROW
DECLARE
v_exists VARCHAR2(1);
v_valid code.valid_code%TYPE;
v_person_id person.id%TYPE;
TYPE Anumber_Type is TABLE of person.registration_number%TYPE INDEX BY binary_INTEGER;
v_NumberList Anumber_Type;
v_primaryAnumber person.primary_number%TYPE;
v_secondaryAnumber consolidated_numbers.secondary_number%TYPE;
v_anumber person.registration_number%TYPE;
BEGIN
IF(INSERTING) THEN
v_person_id := :NEW.person_id;
ELSE
v_person_id := :OLD.person_id;
END IF;
BEGIN
SELECT p.registration_number, p.primary_number, c.secondary_number INTO v_anumber, v_primaryAnumber, v_secondaryAnumber
FROM person p
LEFT JOIN consolidated_numbers c ON p.id = c.person_id WHERE p.id = v_person_id;
END;
BEGIN
SELECT women_act INTO v_exists
FROM person
WHERE id = v_person_id;
EXCEPTION
WHEN NO_DATA_FOUND THEN
v_exists := NULL;
END;
IF v_exists IS NULL AND :NEW.type_id IN (10,20,30,40,50) THEN
IF :NEW.reason NOT IN ('A1','B1') OR (:NEW.reason IN ('A1','B1') AND :NEW.action_date >= '01-JAN-00') THEN
BEGIN
SELECT valid_code INTO v_valid
FROM code
WHERE valid_code = :NEW.reason;
EXCEPTION
WHEN NO_DATA_FOUND THEN
v_exists := null;
END;
IF v_valid IS NOT NULL THEN
SELECT CASE
WHEN EXISTS (SELECT 1 FROM code WHERE valid_code = v_valid)
THEN 'Y' ELSE 'N' END INTO v_exists FROM dual;
END IF;
IF v_exists = 'Y' THEN
select registration_number BULK COLLECT into v_NumberList
FROM person where (registration_number=v_primaryAnumber OR
registration_number=v_anumber OR
registration_number=v_secondaryAnumber OR
primary_number= v_secondaryAnumber OR
primary_number=v_anumber OR
primary_number=v_primaryAnumber ) and (primary_number IS NOT NULL or primary_number <>'000000');
ELSE
select registration_number BULK COLLECT into v_NumberList
FROM person where (registration_number=v_primaryAnumber OR
registration_number=v_anumber OR
registration_number=v_secondaryAnumber OR
primary_number=v_anumber OR
primary_number=v_secondaryAnumber OR
primary_number=v_primaryAnumber OR
primary_number IS NULL);
END IF;
FOR indx IN 1 .. v_NumberList.COUNT
LOOP
update person set women_act = 'X'
where registration_number=v_NumberList(indx) and (women_act<>'X' or women_act IS NULL);
END LOOP;
update person set women_act = 'X'
where registration_number=v_anumber and (women_act<>'X' or women_act IS NULL);
END IF;
END IF;
mmm
END trans_hist_trg;
I need to make this block of code to be my main outer loop to iterate through all the numbers. But I'm unsure how. Please help.
SELECT p.registration_number, p.primary_number, c.secondary_number INTO v_anumber, v_primaryAnumber, v_secondaryAnumber
FROM person p
LEFT JOIN consolidated_numbers c ON p.id = c.person_id WHERE p.id = v_person_id;
Thank you!

Is there a way to feed a column with a concated value from other two columns dynamically incrementing a int in a third column?

I want to create a trigger function to feed a column with a value generated from a concatenation of two other columns with predetermined values. If the value already exists, I would increment a third column accordingly to the concatenated value.
EX:
column1 -> 'aaa'
column2 -> 'bbb'
concatenated value -> 'aaabbb'
column1 -> 'xxx'
column2 -> 'yyy'
concatenated value -> 'xxxyyy'`
If there's already rows with the value 'aaabbb' then the third column would be incremented as:
1 | aaabbb
2 | aaabbb
3 | aaabbb
1 | xxxyyy
2 | xxxyyy
Not sure if you want to save concatenated value. This one does not save it, but you can of course save concatenated value into new.concatenated_column:
create function f_test ()
returns trigger
language plpgsql
as $$
begin
new.column3 := (
select coalesce(max(t.column3) + 1, 1)
from test as t
where t.column1 || t.column2 = new.column1 || new.column2
);
return new;
end;
$$
create trigger tr_test
before insert
on test
for each row execute procedure f_test();
db<>fiddle demo
You can try use this code, its work to only insert. But you can duplicate trigger and change to update too.
create table test ( column1 varchar, column2 varchar, column3 varchar);
CREATE TRIGGER t_before_test
BEFORE insert
ON test
FOR EACH ROW
EXECUTE PROCEDURE fnt_test();
CREATE OR REPLACE FUNCTION fnt_test()
RETURNS trigger LANGUAGE plpgsql AS
$BODY$
declare
v_count int := 0;
BEGIN
select 1 into v_count from test where column1 = NEW.column1 and column2 = NEW.column2;
if ( NEW.column1 is not null and NEW.column2 is not null and v_count > 0) then
NEW.column3 := NEW.column1 || NEW.column2;
end if;
RETURN NEW;
END;
$BODY$;
insert into test ( column1, column2 ) values ( '1', '2');
select * from test;

check if values exist in table - trigger

I am trying to write trigger which will control if a record is already in table or not. If is the record already in table (compare for example by name), so current record set valid='False' and insert new. Is there any way?
This is my idea, but it doesn't work.
create or replace TRIGGER
Check_r
before insert on t$customer
FOR each ROW
declare
v_dup number;
v_com number;
v_id number;
v_id_new number;
begin
v_date:=SYSDATE;
select count(id) INTO v_dup from t$customer where surname=:NEW.surname ;
select count(id) INTO v_com from t$customer where firstname =:NEW.firstname and
address=:NEW.address;
select id into v_id from t$customer where surname=:NEW.surname;
if v_dup > 0 and v_com=0 then
v_id_new:= m$_GET_ID; -- get id
update t$customer set valid = 'False' where id = v_id;
insert into t$customer ( id, surname ,firstname, valid, address ) values (v_id_new,:NEW.surname ,:NEW.firstname, :NEW.valid, :NEW.address);
end if;
if v_dup = 0 then
v_id_new:= m$_GET_ID; -- get id
insert into t$customer ( id, surname ,firstname, valid , address) values (v_id_new,:NEW.surname ,:NEW.firstname, :NEW.valid, :NEW.address);
end if;
end;
You can use a compound trigger, for example:
CREATE OR REPLACE TRIGGER Check_r
FOR INSERT ON t$customer
COMPOUND TRIGGER
TYPE customerRecordType IS RECORD(
surname t$customer.surname%TYPE,
firstname t$customer.firstname%TYPE,
address t$customer.address%TYPE,
ID nubmer);
TYPE customerTableType IS TABLE OF customerRecordType;
customerTable customerTableType := customerTableType();
n NUMBER;
BEFORE STATEMENT IS
BEGIN
customerTable.DELETE; -- not requried, just for better understanding
END STATEMENT;
BEFORE EACH ROW IS
BEGIN
customerTable.EXTEND;
customerTable(customerTable.LAST).surname := :NEW.surname;
customerTable(customerTable.LAST).firstname := :NEW.firstname;
customerTable(customerTable.LAST).address := :NEW.address;
customerTable(customerTable.LAST).ID := m$_GET_ID;
:NEW.ID := customerTable(customerTable.LAST).ID;
END BEFORE EACH ROW;
AFTER STATEMENT IS
BEGIN
FOR i IN customerTable.FIRST..customerTable.LAST LOOP
SELECT COUNT(*) INTO n
FROM t$customer
WHERE surname = customerTable(i).surname;
IF n > 1 THEN
UPDATE t$customer
SET valid = 'False'
WHERE surname = customerTable(i).surname;
END IF;
SELECT COUNT(*) INTO n
FROM t$customer
WHERE firstname = customerTable(i).firstname
AND address = customerTable(i).address;
IF n > 1 THEN
UPDATE t$customer
SET valid = 'False'
WHERE firstname = customerTable(i).firstname
AND address = customerTable(i).address
END IF;
END LOOP;
END AFTER STATEMENT;
END;
/
Please note, this solution is ugly and poor in terms of performance!
But it should give you an impression how it works.
In general you should put all this into a PL/SQL Procedure instead of a trigger.
First, this is trigger for insert. you don't need to write insert statement.
Second, you need to update old record. Just update it with your where clause.
CREATE OR REPLACE TRIGGER Check_r
before insert on t$customer
FOR each ROW
BEGIN
UPDATE t$customer set valid = 'False'
WHERE surname = :NEW.surname
AND firstname =:NEW.firstname;
:NEW.id := m$_GET_ID;
END;

using a select query to create a record in plsql

Probably a really easy question but I created a record that has 3 columns. The third column I am going to assign a value later. The query selects 2 columns for the record. What am I missing? The error that is thrown is that there are "too many values" in my select statment.
create or replace Procedure Pledges3
(IDdonor In Int, flag Out Varchar)
as
type allPledges is record(iddonor dd_pledge.iddonor%type, idstatus dd_status.idstatus%type, flag Varchar(25));
allPledges2 allpledges;
Begin
Select dd_pledge.iddonor, dd_status.idstatus
into allpledges2
from dd_donor
join dd_pledge on dd_donor.iddonor=dd_pledge.iddonor
join dd_status on dd_pledge.idstatus=dd_status.idstatus
where dd_pledge.IDdonor=305;
if allpledges2.idstatus = '10' THEN
Flag := 'True';
elsif allpledges2.idstatus= '20' THEN
Flag := 'False';
End if;
dbms_output.put_line(flag);
End;
Check the below code, corrected:
create or replace Procedure Pledges3
(IDdonor In Int, flag Out Varchar)
as
type allPledges is record(iddonor dd_pledge.iddonor%type, idstatus dd_status.idstatus%type, flag Varchar(25));
allPledges2 allpledges;
Begin
Select dd_pledge.iddonor, dd_status.idstatus, null
into allpledges2
from dd_donor
join dd_pledge on dd_donor.iddonor=dd_pledge.iddonor
join dd_status on dd_pledge.idstatus=dd_status.idstatus
where dd_pledge.IDdonor=305;
if allpledges2.idstatus = '10' THEN
allPledges2.Flag := 'True';
elsif allpledges2.idstatus= '20' THEN
allPledges2.Flag := 'False';
End if;
dbms_output.put_line(allPledges2.flag);
End;