Postgres Trigger - Working with 2 different tables - postgresql

I have 2 tables:
Books - isbn (PK), title, qty-in-stock
Orderlist - ordernum (PK), isbn, quantity
My goal is to insert a record into orderlist and then, if matching isbns, add the quantity to qty-in-stock
My function and trigger is not correct -
CREATE OR REPLACE FUNCTION books_upd() RETURNS trigger as $bookupd$
BEGIN
--IF THE ISBN MATCHES BETWEEN BOOKS AND ORDERLIST
-- ADD THE EXISTING QTY_IN_STOCK (BOOKS) TO QUANTITY (ORDERLIST)
QTY_IN_STOCK:=QTY_IN_STOCK+QUANTITY;
--END IF
RETURN NEW;
$bookupd$ LANGUAGE plpgsql;
CREATE TRIGGER books_upd
BEFORE INSERT OR UPDATE on orderlist
FOR EACH ROW
EXECUTE PROCEDURE books_upd();
Can anyone help?

The trigger function is quite complicated because of different cases of insert and update.
When a user updates the order, then the function should:
add to qty_in_stock the difference between new and old quantities if isbns are the same or
subtract old quantity from qty_in_stock of one book and add new quantity to qty_in_stock of another book when isbns are different.
Additionally, the function should reject changes if the given isbn does not exist in books.
CREATE OR REPLACE FUNCTION books_upd()
RETURNS trigger as $bookupd$
DECLARE
v_quantity int;
BEGIN
v_quantity = NEW.quantity;
IF TG_OP = 'UPDATE' THEN
IF OLD.isbn != NEW.isbn THEN
UPDATE books
SET qty_in_stock = qty_in_stock- OLD.quantity
WHERE isbn = OLD.isbn;
ELSE
v_quantity = NEW.quantity- OLD.quantity;
END IF;
END IF;
UPDATE books
SET qty_in_stock = qty_in_stock+ v_quantity
WHERE isbn = NEW.isbn;
IF NOT FOUND THEN
RAISE EXCEPTION 'Uknown isbn';
END IF;
RETURN NEW;
END;
$bookupd$ LANGUAGE plpgsql;
Read more about NEW, OLD and TG_OP.
The trigger function only for insert is really simple:
CREATE OR REPLACE FUNCTION books_upd()
RETURNS trigger as $bookupd$
BEGIN
UPDATE books
SET qty_in_stock = qty_in_stock+ NEW.quantity
WHERE isbn = NEW.isbn;
RETURN NEW;
END;
$bookupd$ LANGUAGE plpgsql;

Related

How to set a trigger in PostgreSQL?

I have the tables show here -> How can I display if an airline departs from all the airport in my DB in Postgresql?
I've update the airlines table like below
ALTER TABLE airlines
ADD COLUMN count_flight INTEGER;
Now I need to update the column count_flight with the number of flight for each airline that are already stored in volo table.
How can I set a trigger that do what I need + update the value of count_flight every time I insert a new row in volo table?
EDIT
I've set a trigger like below and it works
CREATE FUNCTION update_flight() RETURNS TRIGGER AS $updateN_voli$
DECLARE
airline_name varchar;
BEGIN
IF (TG_OP = 'INSERT') THEN
airline_name = NEW.airline;
UPDATE airlines
SET count_flight = count_flight + 1
WHERE airline_name = airlines.airline_name;
END IF;
RETURN NULL;
END;
$updateN_voli$ LANGUAGE plpgsql;
/* Creo il trigger */
CREATE TRIGGER update_flight
AFTER INSERT ON volo
FOR EACH ROW
EXECUTE PROCEDURE update_flight();
Now I can't figure out how can I update the value with the existing flight. As I said in the comment below I have found a possible solution but is a bit complex if I have a big amount of row in table volo.
I've found the answer:
This is for update the column with existing data in volo table:
DO
$$
DECLARE
a varchar;
BEGIN
FOR a IN select airline from airlines LOOP
UPDATE airlines
SET count_flight = (SELECT COUNT (airline) FROM volo WHERE airline = a)
WHERE airline = a;
END LOOP;
END;
$$;
This is for update te airlines table every time a new flight is add to volo table:
CREATE FUNCTION update_newVoli() RETURNS TRIGGER AS $updateN_voli$
DECLARE
name_airline varchar;
BEGIN
IF (TG_OP = 'INSERT') THEN
name_airline = NEW.airline;
UPDATE compagnie
SET count_flight = count_flight + 1
WHERE name_airline = airlines.airline;
END IF;
RETURN NULL;
END;
$updateN_voli$ LANGUAGE plpgsql;
CREATE TRIGGER update_newVoli
AFTER INSERT ON volo
FOR EACH ROW
EXECUTE PROCEDURE update_newVoli();

Error trigger. Trigger without return. PostgreSQL

I created trigger and function.
CREATE TRIGGER trigger_update_quantity AFTER INSERT ON orderitem
FOR EACH ROW EXECUTE PROCEDURE update_quantity();
CREATE OR REPLACE FUNCTION update_quantity() RETURNS TRIGGER AS $quantity_update$
DECLARE
flower INT;
BEGIN
flower = New.flower_num;
UPDATE product SET quantity = quantity - New.quantity
WHERE flower_num = flower;
END;
$quantity_update$
LANGUAGE plpgsql;
When I try to insert a row into a table. Everything i have the following error.
INSERT INTO "public"."orderitem" ("order_num", "flower_num", "quantity", "total_price") VALUES (?, ?, ?, ?);
[2F005] ERROR: control reached end of trigger procedure without RETURN
Where: PL/pgSQL function update_quantity()
Here's what I would write (not tested):
CREATE OR REPLACE FUNCTION update_quantity() RETURNS TRIGGER AS $quantity_update$
DECLARE
flower INT;
BEGIN
flower = New.flower_num;
UPDATE product SET quantity = quantity - New.quantity
WHERE flower_num = flower;
RETURN NEW;
END;
$quantity_update$
LANGUAGE plpgsql;
CREATE TRIGGER trigger_update_quantity AFTER INSERT ON orderitem
FOR EACH ROW EXECUTE PROCEDURE update_quantity();
how is AFTER TRIGGER, you can return RETURN OLD; or RETURN NULL;

Function error – postgresql

I created this function that checks that total cost of a flight has been calculated correctly. It should multiply the price of seats e.g. 400 by the number of seats a customer has asked for e.g. 2. If the customer inputs the total correctly e.g.800 it should return the query successfully. If they don't input the total correctly e.g. 1000 then it should raise the exception 'price has been calculated incorrectly'.
CREATE FUNCTION check_totalcost() RETURNS trigger AS $check_totalcost$
DECLARE seatprice int;
DECLARE noofseats int;
BEGIN
SELECT priceperseat INTO seatprice
FROM flight
WHERE flightid = flightid;
SELECT numseats INTO noofseats
FROM flightbooking
WHERE flightid = flightid;
IF(seatprice*noofseats != new.totalcost) THEN
RAISE EXCEPTION 'price has been calculated incorrectly';
END IF;
RETURN NEW;
END;
$check_totalcost$ LANGUAGE plpgsql;
/* This is used to run the function above */
CREATE TRIGGER insert_totalcost
BEFORE INSERT OR UPDATE ON flightbooking
FOR EACH ROW EXECUTE PROCEDURE check_totalcost();
When I input the total incorrectly, it raises the exception which is what I want but when I input the total correctly, it also raises the exception when it should return the query successfully.
The queries
SELECT priceperseat INTO seatprice
FROM flight
WHERE flightid = flightid;
SELECT numseats INTO noofseats
FROM flightbooking
WHERE flightid = flightid;
make little sense as the conditions are always true.
I guess you want to get flightid from inserted/updated record. Use the special record NEW:
WHERE flightid = NEW.flightid;
You have one more logical error in your function. The number of seats also should be got from the new record.
CREATE OR REPLACE FUNCTION check_totalcost()
RETURNS trigger AS $check_totalcost$
DECLARE seatprice int;
BEGIN
SELECT priceperseat INTO seatprice
FROM flight
WHERE flightid = new.flightid;
IF seatprice * new.numseats != new.totalcost THEN
RAISE EXCEPTION 'price has been calculated incorrectly';
END IF;
RETURN NEW;
END;
$check_totalcost$ LANGUAGE plpgsql;

How to modify Trigger to update a single attribute in PostgreSQL

Here is my sample table.
CREATE TABLE employee_test(
idTst SERIAL PRIMARY KEY,
monthDownload VARCHAR(6),
changeDate DATE);
I am trying to create a function and trigger that would update changeDate attribute with a current date when monthDownload attribute is updated.
The function I have it works with one problem. It updates all records instead of the one that was updated.
CREATE OR REPLACE FUNCTION downloadMonthChange()
RETURNS TRIGGER AS
$$
BEGIN
IF NEW.monthDownload <> OLD.monthDownload THEN
UPDATE employee_test
SET changeDate = current_date
where OLD.idTst = NEW.idTst;
END IF;
RETURN NEW;
END;
$$
Language plpgsql;
Trigger
Create TRIGGER dataTest
AFTER UPDATE
ON employee_test
FOR EACH ROW
EXECUTE PROCEDURE downloadMonthChange();
When I execute the following Update statement:
UPDATE employee_test SET monthDownload = 'oct12'
WHERE idTst = 1;
All changeDate rows get update with a current date.
Is there a way to have only a row with changed record to have a current date updated.
If you use a before trigger you can write directly to NEW
CREATE OR REPLACE FUNCTION downloadMonthChange()
RETURNS TRIGGER AS
$$
BEGIN
IF NEW.monthDownload <> OLD.monthDownload THEN
NEW.changeDate = current_date;
END IF;
RETURN NEW;
END;
$$
Language plpgsql;
the other option when you must use an after trigger is to include the primary key in the where clause. It appears that you were trying to do this, but you had a spurious OLD in the query. beause of that the where clause was only looking at the record responsible for the trigger call, and not limiting which records were to be updated.
IF NEW.monthDownload <> OLD.monthDownload THEN
UPDATE employee_test
SET changeDate = current_date
where idTst = NEW.idTst;

Postgres trigger function

I need help in Postgres triggers.
I have table with 2 columns:
sold boolean;
id_shop int;
It stores if item is sold, or at which shop its located at.
I need a trigger, if I change the "sold" to true, then it also changes the id_shop to NULL (It can't be in shop if sold...)
I tried different ways, but it doesn't work or gives an error on update cmd...
create or replace function pardota_masina_veikals() RETURNS trigger AS $pardota_masina$
begin
IF NEW.sold=true THEN
update masinas
SET id_shop=null WHERE id=NEW.id;
END IF;
RETURN NEW;
END;
$pardota_masina$ LANGUAGE plpgsql;
CREATE TRIGGER pardota_masina_nevar_but_veikala
AFTER INSERT OR UPDATE ON masinas FOR EACH ROW EXECUTE PROCEDURE pardota_masina_veikals();
First of all you need a before trigger if you want to change a value of the row being updated (or inserted)
Secondly you don't need to "update" the table, just assign the new value to the NEW row:
create or replace function pardota_masina_veikals()
RETURNS trigger
AS
$pardota_masina$
begin
IF NEW.sold=true THEN
NEW.id_shop = NULL;
END IF;
RETURN NEW;
END;
$pardota_masina$
LANGUAGE plpgsql;
CREATE TRIGGER pardota_masina_nevar_but_veikala
BEFORE INSERT OR UPDATE ON masinas
FOR EACH ROW EXECUTE PROCEDURE pardota_masina_veikals();