【问题标题】:Using an SQLite trigger to log field values that change in an update使用 SQLite 触发器记录更新中更改的字段值
【发布时间】:2018-06-08 07:44:50
【问题描述】:

我正在尝试将更新记录到 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,而是看到文字值“”、“: 1”和“:2”。我没有找到很多关于这些编号参数的文档——“参数”部分下的SQLite language page 顺便提到了它们,但提供的信息很少——实际上它甚至没有解释为什么“:1”实际上是second 参数和 "" 是第一个!我应该补充一点,我已经尝试在触发器之外使用非常相似的 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;

返回一个表,其中包含文字值、标记为“a”、“b”、“c”和“d”的列。但触发器中的相同基本结构返回带有冒号的文字字符串,而不是预期的实际值。

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

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

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

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

DB Fiddle

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

更新 2:在玩了一会儿之后,似乎 ""、":1"、":2" 等参数工作正常,只要只有VALUES 表中的常量值。我插入的任何实际值,例如来自新的、旧的等 - 我实际需要的列 - 都被完全忽略,就好像这些列甚至不存在一样!

【问题讨论】:

    标签: sqlite


    【解决方案1】:

    这应该可行:

    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 将字段名称从虚拟行绑定到后面的常量行,然后过滤掉虚拟行。

    【讨论】:

    • 只需在虚拟SELECT 上粘贴WHERE FALSE(或旧版本中的WHERE 0)即可。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-11
    • 1970-01-01
    • 1970-01-01
    • 2016-10-26
    • 1970-01-01
    • 2013-04-14
    相关资源
    最近更新 更多