3

I have two tables called sale and customer. I want to create a trigger that updates the column last_purchase on customer table on each new insert in the sale table.

Table customer: customer_id, name, last_sale, ...
Table sale: sale_id, customer_id, date, ...

CREATE TRIGGER update_last_sale BEFORE INSERT ON sale FOR EACH ROW EXECUTE...

I have started writing but I don't know how to do it.
Could someone help me?

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
dextervip
  • 4,999
  • 16
  • 65
  • 93

3 Answers3

10
CREATE FUNCTION update_customer_last_sale() RETURNS TRIGGER AS $$
BEGIN
    UPDATE customer SET last_sale=now() WHERE cutomer_id=NEW.customer_id;
    RETURN NEW;
END; $$
LANGUAGE plpgsql;

then

CREATE TRIGGER update_last_sale
BEFORE INSERT ON sale
FOR EACH ROW EXECUTE update_customer_last_sale;

NEW is the row which is about to be inserted in the sale table. (For an update row, it would be NEW for how the row will look after the update, and OLD for how the row looks before the update).

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
stew
  • 11,276
  • 36
  • 49
  • The trigger creation doesn't work, that last line needs to be... FOR EACH ROW EXECUTE PROCEDURE update_customer_last_sale(); At least for postgres 10. – Trenton D. Adams Jan 08 '18 at 07:47
  • @stew After being referenced in a [new question](https://stackoverflow.com/q/76019724/939860), I updated this answer. Maybe a bit invasive, so I rolled back. Feel free to apply as you see fit. – Erwin Brandstetter Apr 15 '23 at 00:39
2

Basically, I don't think it is a good idea to store redundant data. The last_sale column in customers is just an aggregate of max(sales.sale_date).

It even gets worse if we use now() to touch customers.last_date. What would happen if we would need to re-insert some historical records (eg to recompute last year's taxes). That's what you get when you store redundant data....

-- modelled after Erwin's version
SET search_path='tmp';

-- DROP TABLE customers CASCADE;
CREATE TABLE customers
    ( id INTEGER NOT NULL PRIMARY KEY
    , name VARCHAR
    , last_sale DATE
    );

-- DROP TABLE sales CASCADE;
CREATE TABLE sales
    ( id INTEGER NOT NULL PRIMARY KEY
    , customer_id INTEGER REFERENCES customers(id)
    , saledate DATE NOT NULL
    );


CREATE OR REPLACE FUNCTION update_customer_last_sale() RETURNS TRIGGER AS $meat$
BEGIN
    UPDATE customers cu
    -- SET last_sale = now() WHERE id=NEW.customer_id
    SET last_sale = (
        SELECT MAX(saledate) FROM sales sa
        WHERE sa.customer_id=cu.id
        )   
    WHERE cu.id=NEW.customer_id
    ;
    RETURN NEW;
END; $meat$
LANGUAGE plpgsql;

CREATE TRIGGER update_last_sale
    AFTER INSERT ON sales
    FOR EACH ROW
    EXECUTE PROCEDURE update_customer_last_sale();


INSERT INTO customers(id,name,last_sale) VALUES(1, 'Dick', NULL),(2, 'Sue', NULL),(3, 'Bill', NULL);


INSERT INTO sales(id,customer_id,saledate) VALUES (1,1,'1900-01-01'),(2,1,'1950-01-01'),(3,2,'2011-12-15');

SELECT * FROM customers;

SELECT * FROM sales;

The results:

 id | name | last_sale  
----+------+------------
  3 | Bill | 
  1 | Dick | 1950-01-01
  2 | Sue  | 2011-12-15
(3 rows)

 id | customer_id |  saledate  
----+-------------+------------
  1 |           1 | 1900-01-01
  2 |           1 | 1950-01-01
  3 |           2 | 2011-12-15
(3 rows)
wildplasser
  • 43,142
  • 8
  • 66
  • 109
-1

I think you want the rule here.

CREATE RULE therule AS ON INSERT TO sale DO ALSO
    (UPDATE customer SET customer.last_sale = now()
           WHERE customer.customer_id=NEW.customer_id);

EDIT: but see the discussion in comments.

Michael Krelin - hacker
  • 138,757
  • 24
  • 193
  • 173
  • Rules are very tricky, you have to understand them very well to use them correctly. It also won't work when using COPY to insert data! – Frank Heikens Dec 13 '11 at 19:10
  • @FrankHeikens, having to understand things you use is not uncommon, I'd say understanding triggers doesn't hurt either ;-) – Michael Krelin - hacker Dec 13 '11 at 19:14
  • Rules are somewhat deprecated and there is a clear recommendation from the Postgres team to use triggers. –  Dec 13 '11 at 19:24
  • @a_horse_with_no_name, care to reveal the source? – Michael Krelin - hacker Dec 13 '11 at 19:33
  • @MichaelKrelin-hacker: e.g. in the chapter regarding `CREATE RULE`: "*If you actually want an operation that fires independently for each physical row, you probably want to use a trigger*". And now that we have `instead of` triggers for views there is really hardly any reason to use them. Additionally they are slower than triggers as well. See the last sentence here: http://www.postgresql.org/docs/current/static/rules-triggers.html –  Dec 13 '11 at 19:52
  • @a_horse_with_no_name, are we looking at the same page? The last sentence is "The summary is, rules will only be significantly slower than triggers if their actions result in large and badly qualified joins, a situation where the planner fails." which is by no means deprecation. The same goes for the `CREATE RULE` chapter, how is that a deprecation? And how does it apply to the case "update the column last_purchase on customer table each new insert in the sale table"? – Michael Krelin - hacker Dec 13 '11 at 20:07
  • here are some more references: http://archives.postgresql.org/pgsql-general/2011-09/msg00982.php or the start of the thread: http://archives.postgresql.org/pgsql-general/2011-09/msg00981.php –  Dec 13 '11 at 21:59
  • Thanks, this is more interesting, though the message you linked explicitly says the opposite: "I think we'd have to have the "something" in place before we consider deprecating rules. At the moment we don't even have a glimmer of a design, so David's statement is many years premature." I'm more or less convinced, I'll edit into the answer the reference to the discussion, but I still think it is a valid answer which may be of use for the future reader and honestly I still don't think it deserves a downvote. – Michael Krelin - hacker Dec 13 '11 at 22:26
  • I did not downvote it. In fact I find rules pretty cool ;) But for this kind of problem I do think triggers are better - especially for someone new to Postgres. –  Dec 13 '11 at 22:32
  • @a_horse_with_no_name, nice to know, because, honestly, I thought it was you. – Michael Krelin - hacker Dec 14 '11 at 08:37