使用SQLite触发器记录更新中更改的字段值

时间:2018-06-07 22:12:24

标签: sqlite

我试图将更新记录到SQLite表中的字段中。我已经能够使用以下内容登录INSERTs

CREATE TRIGGER _test7_INSERT AFTER INSERT ON test7 
BEGIN
    INSERT INTO ChangeLog (rid, field, value, tms)
    SELECT *,CAST((julianday('now') - 2440587.5)*86400000  AS INTEGER)
    FROM (VALUES
            (new.rowid, 'field1', new.field1),
            (new.rowid, 'field2', new.field2),
            (new.rowid, 'field3', new.field3)
     ) sub;
END;

这显然是使用匿名值,我无法找到具体的文档,但它似乎可以创建一个静态表,然后我可以选择将其插入到日志表中。

但是,对于更新,我只想记录实际更改的字段,因此,例如,如果一个表有20个字段,并且由于更新而只更改了1个,那么我只希望将1行添加到更改表中,而不是20行。我有一个触发器,看起来像这样:

CREATE TRIGGER _test7_UPDATE AFTER UPDATE on test7
BEGIN
    INSERT INTO ChangeLog (rid, field, value, tms)
    SELECT "" AS rid,
           ":1" AS field,
           ":2" AS value,
      CAST((julianday('now') - 2440587.5)*86400000  AS INTEGER) AS tms
    FROM (VALUES
        (old.rowid, 'field1', new.field1, new.field1=old.field1),
        (old.rowid, 'field2', new.field2, new.field2=old.field2),
        (old.rowid, 'field3', new.field3, new.field3=old.field3)
     ) sub
    WHERE ":4"=1;
END;

但是在我做更新时没有插入任何内容!如果我删除额外的比较字段并删除" WHERE"子句,我看到添加了三行,但我没有添加ridfieldvalue,而是看到字面值"",&#34 ;:1"和":2"。我还没有找到关于这些编号参数的大量文档 - "参数"下的SQLite language page。部分提到了他们,但提供的信息很少 - 实际上它甚至没有解释为什么":1"实际上是第二个参数和""是第一个!我应该补充一点,我已尝试在触发器之外使用非常相似的SELECT,在VALUES表中放置常量值,它完美运行!具体做法是:

SELECT "" AS a, ":1" AS b, ":2" AS c, ":3" AS d
FROM (VALUES
    (11,22,33,44),
    (111,122,133,144),
    (211,222,233,244)
) sub;

返回一个包含文字值的表格,标有&#34的列;"," b"," c"和" d" 。但是触发器中的相同基本结构会返回带冒号的文字字符串,而不是实际的预期值。

我有一个明智的想法,即创建一个带有额外字段的临时表,选择所有内容,然后选择除真实日志表中的比较之外的所有内容。这不起作用,因为触发器不支持CREATE

我还尝试创建ChangeLog以在比较中有一个额外的字段来保存第五个字段(例如new.fieldx = old.fieldx)但是当我使用WHERE子句只选择更改的字段时,我又回到了没有再插入。

我还有其他方法可以解决这个问题,还是我在处理位置参数方面做错了什么?

更新:我一直在尝试,并发现在某些情况下,位置参数实际上有值,但它们相当荒谬:

DB Fiddle

具体来说,字段""返回第二个参数的值,字段":1"返回第四个参数的值。为什么?如何引用第一个和第三个参数?

更新2 :再多玩一遍后,看来"",":1",": 2"等参数工作正常,只要VALUES表中只有常量值。我插入的任何实际值,例如来自new,old等等 - 我实际需要的列 - 都被完全忽略,好像那些列甚至不存在!

1 个答案:

答案 0 :(得分:0)

这应该有效:

CREATE TRIGGER _test7_UPDATE AFTER UPDATE on test7
BEGIN
    INSERT INTO ChangeLog (rid, field, value, tms)
    SELECT rowid, field, value, 
        CAST((julianday('now') - 2440587.5)*86400000  AS INTEGER)
        FROM (
        SELECT 0 rowid, '' field, 0 value, 0 keep
        UNION ALL
        SELECT sub.* FROM (VALUES
           (old.rowid, 'field1', new.field1, new.field1!=old.field1),
           (old.rowid, 'field2', new.field2, new.field2!=old.field2),
           (old.rowid, 'field3', new.field3, new.field3!=old.field3)
        ) AS sub
    ) WHERE keep=1;
END

诀窍是使用UNION ALL将虚拟行中的字段名称绑定到后面的常量行,然后过滤掉虚拟行。