Frage

I'm trying to create a database with history in mind (experience shows you'll have to do this one day or another).

I've asked here database-design-how-to-handle-the-archive-problem but there's no better anser than the link here.

My problem is about where to do the code and technically, how (MySQL gives me headaches). First I've started doing this in Php: before doing any insert, duplicate the record mark it as "obsolete" then modify the record.

But there's a dependency problem (manytomany and manytoone associations must be updated as well) which implies coding (one way or another) all the dependancies and updates that come with the tables (which is not acceptable).

So I'm thinking about doing all the work on the database server side. This would greatly simplify my Php code.

The problem is that I have to "archive" the current record before modifying it. To do so, the code must be in a trigger "before update".

Here's my code:

DELIMITER ;;

DROP TRIGGER IF EXISTS produit_trigger_update_before;

CREATE TRIGGER produit_trigger_update_before
BEFORE UPDATE ON produit
  FOR EACH ROW BEGIN
    /* */
    INSERT INTO produit SET 
      id_origine      = OLD.id_origine,
      date_v_creation = OLD.date_v_creation,
      date_v_start    = OLD.date_v_debut,
      date_v_end      = NOW(),
      ...
      last_record     = OLD.last_record;

    /* Dependancies : */
    SET @last=LAST_INSERT_ID();
    UPDATE categorie_produit SET id_produit=@last
    WHERE id_produit = OLD.id;
    UPDATE produit_attribut SET id_produit=@last
    WHERE id_produit = OLD.id;
  END;;

DELIMITER ;;

If I get this code working, all my problems are gone. But damn it, it's not working:

mysql> update produit set importance=3;
ERROR 1442 (HY000): Can't update table 'produit' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.
mysql> Bye

In this page there's a working sample, which uses INSTEAD OF UPDATE clause in the trigger. MySQL doesn't seem to support this.

So my question is both conceptual (= have you any other "principle" that could work) and/or technical (= can you make this trigger work).

War es hilfreich?

Lösung

If I get this code working, all my problems are gone. But damn it, it's not working:

As a rule you can't have a trigger on table A trigger inserts into table A - since that could cause an endless loop. (Trigger mutation in Oracle terms)

Personally I would not do this using triggers. Triggers can do "audit logging" - but this is not what you want here.

I suggest you solve it programatically - either with a PHP function or a MySQL stored procedure (whatever your preference) that you call something like "ModifyProduit".

The code would then do basically what you have the trigger above do. (It might be easier to just have the code set date_v_end on the current row, and then insert a completly new row. That way you don't have to mess around with updating your referenced tables)

Andere Tipps

you can do history of a table with an auxiliary table like this (i've done this for many tables on mysql and the speed is very good):

  • table produit_history has the same structure as produit + 2 additional columns: "history_start DATETIME NOT NULL" and "history_stop DATETIME DEFAULT NULL".
  • there are 3 triggers on produit table:
    • AFTER INSERT: in this trigger there is a simple insert into produit_history of the same data with history_start = NOW() and history_stop = NULL (NULL means the current row is valid)
    • AFTER UPDATE: this trigger performs two queries. The first is un update like this:
      UPDATE produit_history set history_stop = NOW() WHERE id_origine = OLD.id_origine AND history_stop IS NULL;
      The second query is an insert identical to the one in the AFTER INSERT trigger.
    • AFTER DELETE: this triggers there is a simple update which is identical to the one in the AFTER UPDATE.

You can then query this history table and obtain snapshots at whatever time you're interested in with the following where condition:

WHERE (history_start <= "interesting_time" AND (history_stop IS NULL OR history_stop > "interesting_time")) 
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top