为什么SQLite给出了无法在表上创建而不是触发器

时间:2015-05-19 10:48:20

标签: triggers sqlite android-sqlite

使用此表

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

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

我尝试了这个触发器:

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是否只允许使用更新触发器。

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

3 个答案:

答案 0 :(得分:6)

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触发器。)

答案 1 :(得分:1)

尝试一下:

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;

答案 2 :(得分:0)

自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)语句。即随后的触发器不会被忽略。
  • 用“ IS”代替“ ==”可以比较可能为NULL的参数。
  • 检查OLD.deleted与NEW.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;