1

Situation

I have a database in PostgreSQL 9.5 used to store object locations by time.

I have a main table named "position" with the columns (only relevant):

  • position_id
  • position_timestamp
  • object_id

It is partitioned into 100 child tables on object_id with the condition:

CREATE TABLE position_object_id_00
( CHECK object_id%100 = 0 ) 
INHERITS ( position );

And so on for the others children. I partitioned with a modulus relation to distribute equally the objects. Each child is indexed on position_id and object_id (two different indexes).

The trigger to redirect inserts on children is:

 CREATE TRIGGER insert_position_trigger
     BEFORE INSERT ON position
     FOR EACH ROW EXECUTE PROCEDURE insert_position();

And the procedure insert_position() looks for the right child table to insert the data, inserts it and then return the NEW object:

CREATE OR REPLACE FUNCTION insert_position() RETURNS TRIGGER AS $insert_position$
  DECLARE

  BEGIN
    --Look for child table
    [...]
    --Insert data in right child table
    [...]

    RETURN NEW;
  END;
$insert_position$ LANGUAGE plpgsql;

I have a summary table object_last_known_position with the same columns that is updated with the trigger:

CREATE TRIGGER update_object_last_known_position 
   AFTER INSERT OR UPDATE ON position 
   FOR EACH ROW
   EXECUTE PROCEDURE update_object_last_known_position();

The procedure update_object_last_known_position() basically checks if the position_timestamp is more recent, then delete the older entry and create a new entry with the data passed on the INSERT or UPDATE query (NEW).

Issue

So these two triggers react to the same event: insert on position, one is before, the other is after Returning new for insert_position() allows me to use NEW in the trigger update_object_last_known_position(), and this is absolutely necessary. But doing that, it also insert the data on the master table position. So my data is now duplicated.

I tried to put the two triggers before, they both execute when I insert data if I let it like that, but if I remove the "return new" from the procedure insert_position(), update_object_last_known_position() is not executed.

I am stuck with this issue and I didn't find a way to execute both of these triggers without filling the master table position when I insert data.

So if you have any ideas, I will really appreciate :)

Thank you for your help!

EDIT

Solution

Thanks to the answer

I "merged" my two triggers: insert_position() now calls update_object_last_known_position directly. For that, I modified update_object_last_known_position to a stored procedure with a parameter. The parameter is the id of the position insert_position() just created, so I am able to find it and retrieve information. (Calling update_object_last_known_position inside the other trigger means we cannot use NEW anymore) And obviously return type for insert_position() is now NULL, and everything works fine :)

Miwauke
  • 47
  • 1
  • 6
  • It would help to have more complete code but it sounds like you are inserting the new row in `update_object_last_known_position()` as well as it being inserted (in order to fire the trigger). Could you just *not* insert the row in `update_object_last_known_position()` – Philip Couling Aug 25 '16 at 09:49
  • The row inserted in the master table is not due to `update_object_last_known_position()` but to `insert_position()`. As it is a before trigger returning NEW, it doesn't "stop" the INSERT, I think. And I need to insert a row in `object_last_known_position`, this is part of the design: I need to store the last position to have better performances when requesting. – Miwauke Aug 25 '16 at 09:59

1 Answers1

0

If I understand you correctly you are trying to:

  1. Stop the insert, and replace it with an insert into another table (determined by the trigger)
  2. Update a summary table (delete/insert) to point to the new row.

Your problem is that 1 stops 2 from happening? That's logical because you've stopped the insert so you've stopped any processing on the insert as well.

So to solve this you have a couple of options (options 1 and 2 are similar)

  1. Call update_object_last_known_position() from insert_position() and only have one trigger
  2. Create a wrapper method for both insert_position() and update_object_last_known_position() and have only one trigger.
  3. Put the trigger for update_object_last_known_position() on all of the tables that insert_position() might insert into.
Philip Couling
  • 13,581
  • 5
  • 53
  • 85