2

我正在尝试创建一个考虑到历史的数据库(经验表明你有一天或另一天必须这样做)。

我在这里问过database-design-how-to-handle-the-archive-problem但没有比这里的链接更好的分析器了。

我的问题是关于在哪里编写代码以及在技术上如何编写代码(MySQL 让我头疼)。首先,我已经开始在 PHP 中执行此操作:在进行任何插入之前,复制记录并将其标记为“过时”,然后修改记录。

但是有一个依赖问题(manytomany 和 manytoone 关联也必须更新),这意味着编码(一种或另一种方式)表附带的所有依赖项和更新(这是不可接受的)。

所以我正在考虑在数据库服务器端完成所有工作。这将大大简化我的 PHP 代码。

问题是我必须在修改当前记录之前“存档”它。为此,代码必须在“更新前”触发器中。

这是我的代码:

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 ;;

如果我让这段代码工作,我所有的问题都消失了。但该死的,它不起作用:

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

在此页面中有一个工作示例,它INSTEAD OF UPDATE在触发器中使用了子句。MySQL 似乎不支持这一点。

所以我的问题既是概念性的(=你有任何其他可以工作的“原则”)和/或技术性的(=你能让这个触发器工作吗)。

4

2 回答 2

0

如果我让这段代码工作,我所有的问题都消失了。但该死的,它不起作用:

通常,您不能将表 A 上的触发器插入表 A 中,因为这可能会导致无限循环。(Oracle 术语中的触发突变)

就我个人而言,我不会使用触发器来做到这一点。触发器可以做“审计日志”——但这不是你想要的。

我建议您以编程方式解决它 - 使用 PHP 函数或 MySQL 存储过程(无论您的偏好如何),您称之为“ModifyProduit”。

然后,代码将基本上完成您上面的触发器所做的事情。(在当前行上设置代码 date_v_end 可能会更容易,然后插入一个全新的行。这样您就不必为更新引用的表而烦恼)

于 2012-06-15T19:26:37.887 回答
0

你可以用这样的辅助表来做一个表的历史(我已经为mysql上的许多表做了这个,速度非常好):

  • produit_history具有与 + 2 个附加列相同的结构produit:“history_start DATETIME NOT NULL”和“history_stop DATETIME DEFAULT NULL”。
  • 表上有 3 个触发器produit
    • 后插入:在此触发器中,使用 history_start = NOW() 和 history_stop = NULL 简单插入produit_history相同的数据(NULL 表示当前行有效)
    • 更新后:此触发器执行两个查询。第一个是这样的联合国更新:
      UPDATE produit_historyset history_stop = NOW() WHERE id_origin = OLD.id_origin AND history_stop 为 NULL;
      第二个查询是与 AFTER INSERT 触发器中的相同的插入。
    • AFTER DELETE:这会触发一个简单的更新,它与 AFTER UPDATE 中的更新相同。

然后,您可以使用以下 where 条件查询此历史记录表并在您感兴趣的任何时间获取快照:

WHERE (history_start <= "interesting_time" AND (history_stop IS NULL OR history_stop > "interesting_time"))
于 2012-12-17T15:08:16.827 回答