【问题标题】:Why does SQLite give Cannot create instead of trigger on table为什么 SQLite 给出Cannot create 而不是trigger on table
【发布时间】:2020-04-06 16:30:39
【问题描述】:

有了这张桌子

create table FOLDER
(
    _ID integer primary key autoincrement,
    NAME text not null,
    PARENT integer not null,
    DELETED integer,
    constraint VALUEOF_folder_deleted check (DELETED == 1 or DELETED isnull) on conflict abort,
    unique (NAME, PARENT) on conflict abort
);

我想用将 DELETED 字段设置为 null 的替换替换存在 NAME PARENT 组合并且 DELETED 设置为 1 的插入。

我试过这个触发器:

CREATE TRIGGER REPLACE_INS_folder instead of insert on FOLDER
when exists (select 1 from FOLDER where NAME == new.NAME and PARENT == new.PARENT and DELETED == 1)
begin
    update FOLDER set DELETED = null where NAME == new.NAME and PARENT == new.PARENT;
end;

但收到: Error: cannot create INSTEAD OF trigger on table: FOLDER

最初我在触发器中有一个语法错误,但收到了同样的错误,这表明发生了一些更严格的事情。本文档https://www.sqlite.org/lang_createtrigger.html 中的图表表明我的触发器是有效的。但是,A trigger may be specified to fire whenever a DELETE, INSERT, or UPDATE of a particular database table occurs, or whenever an UPDATE occurs on on one or more specified columns of a table. 文本的第二部分让我想知道 WHEN 是否只允许用于更新触发器。

请注意,这是一个表上的触发器,而不是视图上的触发器。

【问题讨论】:

    标签: triggers sqlite android-sqlite


    【解决方案1】:

    documentation 说:

    通过在 CREATE TRIGGER 语句中指定 INSTEAD OF,可以在视图以及普通表上创建触发器。

    这是相当具有误导性的。这句话其实想说的是

    • 在普通表上,您只能使用 BEFORE 和 AFTER 触发器,但是
    • 在视图上,您​​只能使用 INSTEAD OF 触发器。

    您不能对表使用 INSTEAD OF 触发器。将其更改为视图:

    CREATE TABLE FolderTable(...);
    CREATE VIEW Folder AS SELECT * FROM FolderTable;
    CREATE TRIGGER ... INSTEAD OF INSERT ON Folder ...;
    

    (然后您还需要 INSTEAD OF UPDATE/DELETE 触发器。)

    【讨论】:

    • 不是很有帮助的文档。我寻找报告文档错误的链接,但没有。我将针对表格编写测试代码。
    • 啊啊啊啊……现在说得通了!谢谢!
    • 知道为什么INSTEAD OF 仅用于查看吗?
    • @PaulCalabro:在 SQL 标准中定义:“如果指定了 INSTEAD OF,则 T 应为查看表。...否则,T 应为基表...” viewed table 表示视图。 基表表示普通表。 INSTEAD OF 只能在 Oracle 和 PostgreSQL 中的视图上声明。
    【解决方案2】:

    试试这个:

    CREATE TRIGGER replace_ins_folder
    BEFORE INSERT ON folder
    WHEN EXISTS (SELECT 1 FROM folder
                 WHERE name == new.name
                   AND parent == new.parent
                   AND deleted == 1)
    BEGIN
        UPDATE folder
        SET deleted = NULL
        WHERE name == new.name AND parent == new.parent;
    
        SELECT RAISE(IGNORE);
    END;
    

    【讨论】:

    • 是的,看起来可以。我不再安装 SQLite,因此无法对其进行测试。为了完整起见, SELECT RAISE(IGNORE) 会导致任何后续触发器被忽略,但在我原来的场景中,没有。
    【解决方案3】:

    自 SQLite 3.24.0 (2018-06-04) 起,请求的功能被优雅地封装在 UPSERT 方案 INSERT..ON CONFLICT:https://www.sqlite.org/lang_UPSERT.html

    但是,就我而言,我用来编写数据库客户端的编程语言无法识别“ON CONFLICT”子句。

    因此,我实施了 Artem Odnovolov 建议的修改版本。它有效,但它需要对我的方案进行一些调整,这比 Steve Waring 发布的方案更通用。通用版本是:

    • 使用“INSERT OR IGNORE”而不是“INSERT”在表中插入新行。这取代了 SELECT RAISE(IGNORE) 语句。 IE。随后的触发器不会被忽略。
    • 将“==”替换为“IS”允许比较可能为 NULL 的参数。
    • 对照 NEW.deleted 检查 OLD.deleted 的值,以获得更一般的 UPSERT 行为。

    仍然以 Steve Waring 的表格为例,表格创建代码如下所示:

    CREATE TABLE folder(id      integer PRIMARY KEY,
                        name    text,
                        parent  integer,
                        deleted integer,
                        UNIQUE(name, parent)
                       );
    

    还有 BEFORE INSERT 触发器:

        CREATE TRIGGER replace_ins_folder
        BEFORE INSERT ON folder
    
        WHEN EXISTS (SELECT 1 FROM folder       --check for violation of UNIQUE constraint
                     WHERE name IS NEW.name
                       AND parent IS NEW.parent
                    )
        BEGIN
            UPDATE folder
            SET deleted = NEW.deleted
            WHERE name IS NEW.name 
              AND parent IS NEW.parent 
              AND deleted IS NOT NEW.deleted; -- only do update when there is something to change
                                              -- otherwise subsequent ON UPDATE triggers may fire when not intended
        END;
    

    【讨论】:

      猜你喜欢
      • 2013-11-17
      • 2013-11-21
      • 1970-01-01
      • 1970-01-01
      • 2020-05-19
      • 1970-01-01
      • 2015-01-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多