Home > Enterprise >  Updated Record Not Showing Up in a Table After a Trigger Update
Updated Record Not Showing Up in a Table After a Trigger Update

Time:01-02

I created a table, payment_detail, based on two other tables (customer and payment) as follows:

CREATE TABLE IF NOT EXISTS payment_detail AS
SELECT customer.customer_id,
   customer.first_name || ' ' || customer.last_name AS customer_name,
   to_char(payment_date, 'Mon YYYY') AS month_yr,
   SUM(payment.amount) AS total_amount,
   COUNT(payment.amount) AS payments_count
FROM payment
INNER JOIN customer ON payment.customer_id = customer.customer_id
GROUP BY 1,3
ORDER BY
   MAX(date_trunc('year', payment_date)) DESC,
   MAX(date_trunc('month', payment_date)) DESC,
   MAX(amount) DESC;

The resulting table looks like this:

enter image description here

Goal: Create a trigger function to update the payment_detail table upon an UPDATE and an INSERT operation.

Trigger for the UPDATE operation:

CREATE OR REPLACE FUNCTION update_payment_detail()
   RETURNS TRIGGER LANGUAGE PLPGSQL AS
$$
BEGIN
   UPDATE payment_detail SET
   customer_id = NEW.customer_id,
   customer_name = first_name || ' ' || last_name,
   month_yr = to_char(NEW.payment_date, 'Mon YYYY'),
FROM
   customer
WHERE
   customer.customer_id = NEW.customer_id AND
   payment_detail.customer_id = OLD.customer_id;
RETURN NULL;
END;
$$

CREATE TRIGGER update_payment_detail
   AFTER UPDATE
   ON payment
   FOR EACH ROW
   EXECUTE PROCEDURE update_payment_detail();

The problem with the above Trigger is that, although it updates the payment table successfully, the updated record no longer shows up in the payment_detail table. For instance, if I perform an UPDATE as follows:

UPDATE payment
SET amount = 9.00
WHERE customer_id = 60 AND payment_id = 32034;

The payment_detail table no longer shows the record for 'Mildred Bailey' as pictured above, in the first row of the table. But if I DROP TABLE payment_detail; and re-run the table creation, then the record appears.

How would I prevent this from happening? I think the answer is rooted in the condition of my WHERE clause, but I could use some guidance on this if that's the case.

Next, is the trigger for the INSERT operation:

CREATE OR REPLACE FUNCTION insert_payment_detail()
   RETURNS TRIGGER LANGUAGE PLPGSQL AS
$$
BEGIN
   INSERT INTO payment_detail (customer_id, month_yr, total_amount, payments_count)
   SELECT
      NEW.customer_id,
      to_char(NEW.payment_date, 'Mon YYYY') AS month_yr,
      SUM(NEW.amount) AS total_amount,
      COUNT(NEW.amount) AS payments_count
   FROM customer, payment
   WHERE customer.customer_id = NEW.customer_id AND
   to_chair(payment.payment_date, 'Mon YYYY') = to_char(NEW.payment_date, 'Mon YYYY');
RETURN NULL;
END;
$$

CREATE TRIGGER insert_payment_detail
   AFTER INSERT ON payment
   FOR EACH ROW EXECUTE PROCEDURE insert_payment_detail();

If I apply an INSERT as follows:

INSERT INTO payment (customer_id staff_id, rental_id, amount, payment_date)
VALUES (60, 2, 14741, 4.00, now());

I verify that the payment table holds the new record; however when I run SELECT * FROM payment_detail, it does not show the new record. So in both my UPDATE and INSERT trigger functions, the targeted table just omits the updated/inserted record, until I delete and recreate the table.

What am I doing wrong?

CodePudding user response:

Trigger(s) for UPDATE

(a) You may have an error in your trigger function update_payment_detail(), try to replace

customer_name = first_name || ' ' || last_name

by

customer_name = NEW.first_name || ' ' || NEW.last_name

(b) The columns of the table payment_detail are copied from both tables customer and payment, so you probably need 2 different trigger functions for update :

CREATE OR REPLACE FUNCTION update_payment_detail()
   RETURNS TRIGGER LANGUAGE PLPGSQL AS
$$
BEGIN
   UPDATE payment_detail SET
--   customer_id = NEW.customer_id, -- NEW.customer_id is not possible here if it is a column of the table customer; NEW only refers to the columns of the table payment called by the trigger !
--   customer_name = NEW.first_name || ' ' || NEW.last_name, -- idem as above if these columns come from the table customer
   month_yr = to_char(NEW.payment_date, 'Mon YYYY')
--FROM
--   customer
WHERE
--   customer.customer_id = NEW.customer_id AND
   payment_detail.customer_id = OLD.customer_id ;
RETURN NULL;
END;
$$

CREATE TRIGGER update_payment_detail
   AFTER UPDATE
   ON payment
   FOR EACH ROW
   EXECUTE PROCEDURE update_payment_detail();

CREATE OR REPLACE FUNCTION update_customer_detail()
   RETURNS TRIGGER LANGUAGE PLPGSQL AS
$$
BEGIN
   UPDATE payment_detail SET
   customer_id = NEW.customer_id,
   customer_name = NEW.first_name || ' ' || NEW.last_name
--   month_yr = to_char(NEW.payment_date, 'Mon YYYY') -- NEW.payment_date is not possible here if it is a column of the table payment; NEW only refers to the columns of the table customer called by the trigger !
--FROM
--   customer
WHERE
   customer.customer_id = OLD.customer_id
--   payment_detail.customer_id = OLD.customer_id ;
RETURN NULL;
END;
$$

CREATE TRIGGER update_customer_detail
   AFTER UPDATE
   ON customer
   FOR EACH ROW
   EXECUTE PROCEDURE update_customer_detail();

Trigger for INSERT

(c) The reference to the table customer sounds like useless in this trigger function. You can try this :

CREATE OR REPLACE FUNCTION insert_payment_detail()
   RETURNS TRIGGER LANGUAGE PLPGSQL AS
$$
BEGIN
   INSERT INTO payment_detail (customer_id, month_yr, total_amount, payments_count)
   SELECT
      NEW.customer_id,
      to_char(NEW.payment_date, 'Mon YYYY'),
      SUM(amount),
      COUNT(*)
   FROM payment
   WHERE customer_id = NEW.customer_id AND
   to_char(payment_date, 'Mon YYYY') = to_char(NEW.payment_date, 'Mon YYYY');
RETURN NULL;
END;
$$

CREATE TRIGGER insert_payment_detail
   AFTER INSERT ON payment
   FOR EACH ROW EXECUTE PROCEDURE insert_payment_detail();

(d) Depending on the primary key of the table payment_detail, it is possible that you may have some conflicts when inserting 2 rows in table payment with the same customer_id and the same to_char(payment_date, 'Mon YYYY') value. In order to prevent any conflict, you can add the clause ON CONFLICT DO UPDATE ...

(e) Finally, the table payment_detail could be replaced by a view as proposed by @Adrian Klaver

  • Related