why does my trigger function cause a null error? - sql

PostgreSQL.
My trigger func:
CREATE OR REPLACE FUNCTION new_purchase() RETURNS TRIGGER AS $psql$
BEGIN
IF NOT EXISTS(
SELECT 1
FROM currents c
WHERE c.name = new.name
)
THEN
INSERT INTO currents (name, qty)
VALUES (new.name, new.qty);
ELSE
UPDATE currents
SET
qty = qty + new.qty
WHERE
name = new.name;
END IF;
return new;
END;
$psql$ language plpgsql;
Then my trigger:
create trigger new_purchase_trigger
after insert on purchases
for each statement
execute procedure new_purchase();
my two tables with which i am manipulating:
Purchases
CREATE TABLE purchases (
name VARCHAR,
date TIMESTAMP,
qty INTEGER,
price NUMERIC,
about VARCHAR
)
Currents
CREATE TABLE currents (
name VARCHAR,
qty INTEGER
)
My INSERT:
INSERT INTO purchases (name, date, qty, price, about)
VALUES ('cheese', '2022-07-31', 1000, 11500, 'holland');
And finally the error when i am adding data to purchases via INSERT INTO - VALUES:
ERROR: NULL value in column "name" of relation "currents" violates the NOT NULL constraint
DETAIL: Error string contains (null, null).
CONTEXT: SQL statement: "INSERT INTO currents (name, qty)
VALUES(new.name, new.qty)"
PL/pgSQL new_purchase() function, line 9, statement SQL statement
SQL state: 23502

You need a ROW level trigger for what you are trying to achieve. In a statement level trigger (for each statement) the record new is not populated:
create trigger new_purchase_trigger
after insert on purchases
for each row
execute procedure new_purchase();
Note that you can simplify your trigger function if you declare currents.name as the primary key:
CREATE OR REPLACE FUNCTION new_purchase()
RETURNS TRIGGER
AS $psql$
BEGIN
INSERT INTO currents (name, qty)
VALUES (new.name, new.qty)
on conflict (name) do update
set qty = currents.qty + excluded.qty;
return new;
END;
$psql$
language plpgsql;

Related

Postgresql :Syntax issue in MERGE with JSON param in stored function

Facing syntax issue when I have json input, which needs to be insert/update values from JSON into the table which is a view
Expected
When Primary id matched with Item_id (PK) - update its corresponding values
When Primary id not matched with Item_id (PK) - insert into the view
Sample SP
drop function if exists sp_post_itemsx;
CREATE FUNCTION sp_post_itemsx(i_data jsonb)
RETURNS VOID
AS $function$
BEGIN
MERGE INTO vw_item_status_detail
USING
(SELECT
i_data->>'fulfillerId' fulfillerId,
t->>'itemId' itemId,
i_data->>'orderId' orderId,
1000,
t->>'skuCode' skuCode,
t->>'decorationTechnology' decorationTechnology,
(t->>'quantity')::numeric quantity ,
NOW()
FROM jsonb_array_elements(i_data -> 'items')) as t ON item_id = t.itemId
WHEN MATCHED THEN UPDATE SET
vw.quantity = t.quantity
WHEN NOT MATCHED THEN
INSERT INTO vw_item_status_detail(
fulfiller_id,
item_id,
order_id,
status_id,
sku_code,
decoration_technology,
quantity,
item_updated_time)
SELECT
i_data->>'fulfillerId' fulfillerId,
t->>'itemId' itemId,
i_data->>'orderId' orderId,
1000,
t->>'skuCode' skuCode,
t->>'decorationTechnology' decorationTechnology,
(t->>'quantity')::numeric quantity ,
NOW()
FROM jsonb_array_elements(i_data -> 'items') t ;
END;
$function$
LANGUAGE plpgsql;
ERROR: "vw_item_status_detail" is not a known variable
Not sure what is wrong with the syntax or MERGE doesn't work with insert for views table

how can i access a value that's being updated by a trigger?

here's the value of ACCOUNT_NUMBER that has been generated by a sequence and inserted in ACCOUNTS table by ACCOUNT_NUMBER_TRIG trigger that i need to insert it into the TRANSACTION TABLE by the trigger ACCOUNTS_TRANSCATION_TRIG_1
CREATE OR REPLACE TRIGGER ACCOUNT_NUMBER_TRIG
BEFORE INSERT
ON ACCOUNTS
FOR EACH ROW
WHEN (NEW.ACCOUNT_NUMBER is not null)
DECLARE
V_ACC_NO ACCOUNTS.ACCOUNT_NUMBER%TYPE;
BEGIN
SELECT ACCOUNT_NO_SEQ.nextvaL INTO V_ACC_NO FROM DUAL;
:NEW.ACCOUNT_NUMBER := V_ACC_NO;
END ACCOUNT_NUMBER_TRIG;
------------------------------------------------------------------------------
CREATE OR REPLACE TRIGGER ACCOUNTS_TRANSCATION_TRIG_1 AFTER
INSERT ON ACCOUNTS FOR EACH ROW DECLARE CURSOR ACCOUNTS_CUR IS
SELECT ACCOUNT_NUMBER FROM ACCOUNTS;
DECLARE
TEMP_1 NUMBER(5,0);
BEGIN
SELECT ACCOUNTS.ACCOUNT_NUMBER FROM INSERTED INTO TEMP_1
OPEN ACCOUNTS_CUR;
INSERT
INTO TRANSACTIONS VALUES
(
SYSDATE,
- :NEW.ACCOUNT_NUMBER,
'NEW ACCOUNT',
0
);
CLOSE ACCOUNTS_CUR;
END ACCOUNTS_TRANSCATION_TRIG_1;
CREATE TABLE accounts(
ACCOUNT_NUMBER number,
ACCOUNT_NAME varchar2(20)
);
CREATE SEQUENCE ACCOUNT_NO_SEQ;
CREATE OR REPLACE TRIGGER ACCOUNT_NUMBER_TRIG
BEFORE INSERT
ON ACCOUNTS
FOR EACH ROW
WHEN (NEW.ACCOUNT_NUMBER is not null)
BEGIN
:NEW.ACCOUNT_NUMBER :=ACCOUNT_NO_SEQ.nextvaL;
END ACCOUNT_NUMBER_TRIG;
/
CREATE TABLE transactions(
TR_DATE date,
TR_ACCOUNT_NUMBER number,
TR_TYPE varchar2(20),
TR_somenumber int
);
CREATE OR REPLACE TRIGGER ACCOUNTS_TRANSCATION_TRIG_1 AFTER
INSERT ON ACCOUNTS FOR EACH ROW
BEGIN
INSERT INTO TRANSACTIONS( TR_DATE, TR_ACCOUNT_NUMBER, TR_TYPE, TR_somenumber )
VALUES
(
SYSDATE,
:NEW.ACCOUNT_NUMBER,
'NEW ACCOUNT',
0
);
END ACCOUNTS_TRANSCATION_TRIG_1;
/
INSERT INTO accounts( ACCOUNT_NUMBER, ACCOUNT_NAME ) VALUES (1111,'My Name' );
select * from accounts;
ACCOUNT_NUMBER ACCOUNT_NAME
-------------- --------------------
2 My Name
select * from transactions;
TR_DATE TR_ACCOUNT_NUMBER TR_TYPE TR_SOMENUMBER
---------- ----------------- -------------------- -------------
2017/07/11 2 NEW ACCOUNT 0
You can use CURVAL to get the most recent value returned by NEXTVAL:
CREATE OR REPLACE TRIGGER ACCOUNTS_TRANSCATION_TRIG_1 AFTER
INSERT ON ACCOUNTS FOR EACH ROW DECLARE CURSOR ACCOUNTS_CUR IS
BEGIN
INSERT
INTO TRANSACTIONS VALUES
(
SYSDATE,
- ACCOUNT_NO_SEQ.curval,
'NEW ACCOUNT',
0
);
CLOSE ACCOUNTS_CUR;
END ACCOUNTS_TRANSCATION_TRIG_1;
However in this case there is no need, as it has been used to set the ACOUNT_NUMBER:
INSERT
INTO TRANSACTIONS VALUES
(
SYSDATE,
- :NEW.ACCOUNT_NUMBER,
'NEW ACCOUNT',
0
);
BTW unless you are on an old version of Oracle this should work for first trigger:
CREATE OR REPLACE TRIGGER ACCOUNT_NUMBER_TRIG
BEFORE INSERT
ON ACCOUNTS
FOR EACH ROW
WHEN (NEW.ACCOUNT_NUMBER is not null)
BEGIN
:NEW.ACCOUNT_NUMBER := ACCOUNT_NO_SEQ.nextvaL;
END ACCOUNT_NUMBER_TRIG;
(I suspect the WHEN clause is wrong - should be when is null?)

How to create function that updates changes in one table from another table?

I have this function that I found here:
Insert trigger to Update another table using PostgreSQL
CREATE TABLE table1
(
id integer NOT NULL,
name character varying,
CONSTRAINT table1_pkey PRIMARY KEY (id)
)
CREATE TABLE table2
(
id integer NOT NULL,
name character varying
)
CREATE OR REPLACE FUNCTION function_copy() RETURNS TRIGGER AS
$BODY$
BEGIN
INSERT INTO
table2(id,name)
VALUES(new.id,new.name);
RETURN new;
END;
$BODY$
language plpgsql;
CREATE TRIGGER trig_copy
AFTER INSERT ON table1
FOR EACH ROW
EXECUTE PROCEDURE function_copy();
If I insert these two rows:
insert into table1 values (1, 'Andrey');
insert into table1 values (2, 'Mariza');
Then they also go into table2.
My problem is when I do an update on a value:
update table1 set name = 'Andi' where id = '1';
nothing happens in table2.
How can I create a function that updates changes in table?
To support UPDATE you can do this:
Trigger:
CREATE TRIGGER trig_copy
AFTER INSERT OR UPDATE ON table1
FOR EACH ROW
EXECUTE PROCEDURE function_copy();
Function:
CREATE OR REPLACE FUNCTION function_copy() RETURNS TRIGGER AS
$BODY$
BEGIN
if TG_OP='INSERT' then
INSERT INTO table2(id,name) VALUES(new.id,new.name);
end if;
if TG_OP='UPDATE' then
Update table2 set name=new.name where id=old.id;
end if;
RETURN new;
END;
$BODY$
language plpgsql;
create or replace trigger trig_copy
after insert or update
on table1
begin
merge into table2 t2
using (
select
id,
name
from
table1 t1) t_new
on (t2.id = t_new.id)
when matched then
update
set
t2.name = t_new.name
where
t2.id = t_new.id
WHEN NOT MATCHED then
insert (id,name) values(t_new.id,t_new.name);
end;
This will be applied to all records, not only updated record.
If U want to update/insert only one record Create trigger
referencing new as new
for each row
and use :new.id :new.name , to find record You want in t2.
Thanks to all for help. This is the answer that had worked for me.
I took it from Elads and Justas solutions:
CREATE TRIGGER trig_copy
AFTER INSERT OR UPDATE ON table1
FOR EACH ROW
EXECUTE PROCEDURE function_copy();
CREATE OR REPLACE FUNCTION function_copy() RETURNS TRIGGER AS
$BODY$
BEGIN
if TG_OP='INSERT' then
INSERT INTO table2
(SELECT * FROM table1
WHERE id NOT IN
(SELECT id FROM table2));
end if;
if TG_OP='UPDATE' then
Update table2 set name=new.name where id=old.id;
end if;
RETURN new;
end;
$BODY$
language plpgsql;

PL/SQL trigger to insert next value

I created a trigger which works like when I update/insert a row in one table, an insert of a row will a done in another table which contains a primary key.
Now when I insert a row in the first table I want the trigger to check the last value of primary key of another table and if that is null or '-' then I've to insert 1 into that primary key column so as to insert the remaining values.
I've written the code as follows:
create or replace trigger "T1"
AFTER
insert or update on "buses"
for each row
begin
-- Here I want to check the V_id on vehicles table, if that is null or '-' then insert V_id as 1 along with the below insert statement.
if :NEW."b_key" is not null then
INSERT INTO vehicles (b_KEY,B_NAME,ADDRESS_1,CITY,STATE,ZIP,PHONE,WEBSITE) VALUES (:new.b_KEY,:new.b_NAME,:new.ADDRESS_1,:new.CITY,:new.STATE,:new.ZIP,:new.PHONE,:new.WEBSITE);
end if;
end;
How to find the last b_id in the vehicles table, so that if that value is null or '-' insert b_id as 1, followed by the above insert statement in the same row.
By adding another trigger we can do that as follows:
create or replace TRIGGER "B_VEHICLES"
before insert on "buses"
for each row
declare b_number number;
begin
select max(B_ID) into b_number from Vehicles;
if :OLD."B_ID" is null and b_number is null then
select 1 into :new."B_ID" from dual;
else select b_number + 1 into :new."B_ID" from dual;
end if;
end;​

Get ID of last inserted row and use it to insert into another table in a stored procedure

I have the following stored procedure which we use to insert data into a table:
CREATE OR REPLACE PROCEDURE mySproc
(
invoiceId IN NUMBER
customerId IN NUMBER
)
IS
BEGIN
INSERT INTO myTable (INVOICE_ID)
VALUES (invoiceId);
END mySproc;
/
What I am trying to do is to get the last inserted ID (this is the primary key field on myTable and auto incremented using a sequence) and insert it into another table, I have tried the following but could not get it working:
CREATE OR REPLACE PROCEDURE mySproc
(
invoiceId IN NUMBER
customerId IN NUMBER
)
IS
BEGIN
INSERT INTO myTable (INVOICE_ID)
VALUES (invoiceId)
returning id into v_id;
INSERT INTO anotherTable (ID, customerID)
VALUES (v_id, customerId);
END mySproc;
/
I am getting this error: [Error] PLS-00049 (59: 26): PLS-00049: bad bind variable 'V_ID' I think I need to declare v_id somewhere but I tried before and after the BEGIN statement but that gave another error.
Any ideas as to how to do this?
Thanks
Change your procedure to
CREATE OR REPLACE PROCEDURE mySproc
(
invoiceId IN NUMBER, -- Added comma
customerId IN NUMBER
)
IS
v_id NUMBER; -- ADDED
BEGIN
INSERT INTO myTable (INVOICE_ID)
VALUES (invoiceId)
returning id into v_id;
INSERT INTO anotherTable (ID, customerID)
VALUES (v_id, customerId);
END mySproc;
Share and enjoy.