SQLite外键,删除级联和SQLITE_CONSTRAINT

时间:2016-07-15 04:04:17

标签: node.js sqlite

概述: 我有一个父/子表关系,其中子可能包含2:n个记录,其中FK返回到父级。 尝试从父级删除时,出现SQLITE_CONSTRAINT错误。这是意料之外的,因为我启用了FK,让孩子注册了ON DELETE CASCADE,以及一个足够新的SQLite版本。

但是:我的子表最初没有ON DELETE CASCADE。在将数据添加到父/子后,我添加了(并启用了FK)。从那里,我改名原来的孩子&使用约束创建了一个新表,最后移动到新表。

表格布局如下:

CREATE TABLE IF NOT EXISTS message (
    message_id              INTEGER PRIMARY KEY, 
    area_tag                VARCHAR NOT NULL,
    message_uuid            VARCHAR(36) NOT NULL, 
    reply_to_message_id     INTEGER,
    to_user_name            VARCHAR NOT NULL,
    from_user_name          VARCHAR NOT NULL,
    subject, /* FTS @ message_fts */
    message, /* FTS @ message_fts */
    modified_timestamp      DATETIME NOT NULL,
    view_count              INTEGER NOT NULL DEFAULT 0,
    UNIQUE(message_uuid) 
);

CREATE INDEX IF NOT EXISTS message_by_area_tag_index
ON message (area_tag);

CREATE VIRTUAL TABLE IF NOT EXISTS message_fts USING fts4 (
    content="message",
    subject,
    message
);

CREATE TRIGGER IF NOT EXISTS message_before_update BEFORE UPDATE ON message BEGIN
    DELETE FROM message_fts WHERE docid=old.rowid;
END;

CREATE TRIGGER IF NOT EXISTS message_before_delete BEFORE DELETE ON message BEGIN
        DELETE FROM message_fts WHERE docid=old.rowid;
END;

CREATE TRIGGER IF NOT EXISTS message_after_update AFTER UPDATE ON message BEGIN
    INSERT INTO message_fts(docid, subject, message) VALUES(new.rowid, new.subject, new.message);
END;

CREATE TRIGGER IF NOT EXISTS message_after_insert AFTER INSERT ON message BEGIN
    INSERT INTO message_fts(docid, subject, message) VALUES(new.rowid, new.subject, new.message);
END;

CREATE TABLE IF NOT EXISTS message_meta (
    message_id      INTEGER NOT NULL,
    meta_category   INTEGER NOT NULL,
    meta_name       VARCHAR NOT NULL,
    meta_value      VARCHAR NOT NULL,
    UNIQUE(message_id, meta_category, meta_name, meta_value), 
    FOREIGN KEY(message_id) REFERENCES message(message_id) ON DELETE CASCADE
);

启动时,在连接到数据库之后,我确保启用了FK:

PRAGMA foreign_keys = ON;

其他细节:

  • SQLite版本:3.7.17
  • 访问权限:node-sqlite3
  • 确切错误:Error: SQLITE_CONSTRAINT: FOREIGN KEY constraint failed

这是因为我后来添加了约束吗?(参见更新1) 如何在不丢失数据的情况下解决此问题?

更新1
我可以确认只有选择的消息(我相信,消息 ON DELETE CASCADE之前的消息添加到message_meta中)会导致约束错误。其他人删除就好并正确取出相关的message_meta记录。

1 个答案:

答案 0 :(得分:0)

回答我自己的问题 - 经过几个小时尝试各种各样的事情后,我找到了问题:

  • 当我最初添加ON DELETE CASCADE子句时,我是通过将原始message_meta表重命名为message_meta_backup,使用该子句创建新表,然后移动数据来实现的。进入它:SELECT * FROM message_meta_backup INSERT INTO message_meta;。我没有做的是删除备份表。

  • 由于#1或其他相关内容,我的数据库内部的内容已损坏或混淆。

我尝试了什么(不起作用):

  • REINDEX;
  • 只需删除备用表:DROP TABLE message_meta_backup;
  • ......以及我忘记的各种其他事情:)

DID的工作原理:
最终结果是使用sqlite3 shell .drop命令删除备份表并完全重建数据库的组合:

> sqlite3 db/message.sqlite3 
SQLite version 3.7.17 2013-05-20 00:56:22
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> drop table message_meta_backup;
sqlite> .quit
> sqlite3 db/message.sqlite3 ".dump" >> message_dump.sql
rm db/message.sqlite3 
> cat message_dump.sql | sqlite3 db/message.sqlite3

我现在能够DELETE FROM message ...并将其正确地将删除级联到message_meta而没有令人讨厌的错误:

sqlite> DELETE FROM message WHERE message_id IN(SELECT message_id FROM message WHERE area_tag='some_area' ORDER BY message_id desc limit -1 offset 200);
sqlite>

(没有给出错误!)