sqlite - SQLite递归CTE UPDATE在3.18.0中运行,SQL_CONSTRAINT在3.19.0中运行

标签 sqlite common-table-expression

我们有一个SQLite CTE UPDATE,它在3.18.0及更高版本中均可用,但在3.19.0中由于FOREIGN KEY约束而开始失败(错误19)。

以下是显示行为的示例玩具数据库。

sqlite> .version
SQLite 3.18.0 2017-03-28 18:48:43 424a0d380332858ee55bdebc4af3789f74e70a2b3ba1cf29d84b9b4bcf3e2e37
sqlite> .dump t2
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE T2 (Id INTEGER PRIMARY KEY AUTOINCREMENT , ParentId INTEGER NOT NULL , Name TEXT NOT NULL , FavoriteState INT NOT NULL DEFAULT 0, FOREIGN KEY (ParentId) REFERENCES T2(Id) );
INSERT INTO T2 VALUES(1,0,'/',2);
CREATE INDEX idx_pid_t2 ON T2 (ParentId);
CREATE INDEX idx_files_favstate_t2 on T2 (FavoriteState);
CREATE UNIQUE INDEX idx_pnc_t2 ON T2 (ParentId, Name);
COMMIT;
sqlite> PRAGMA foreign_keys=ON;
sqlite> WITH RECURSIVE under_favorite_path(parent) AS ( VALUES(1) UNION ALL SELECT T2.Id FROM T2 JOIN under_favorite_path ON T2.ParentId = under_favorite_path.parent ) UPDATE T2 SET FavoriteState = 1
WHERE Id IN under_favorite_path;
sqlite> select * from t2;
1|0|/|1
sqlite> WITH RECURSIVE under_favorite_path(parent) AS ( VALUES(1) UNION ALL SELECT T2.Id FROM T2 JOIN under_favorite_path ON T2.ParentId = under_favorite_path.parent ) UPDATE T2 SET FavoriteState = 2
WHERE Id IN under_favorite_path;
sqlite> select * from t2;
1|0|/|2


在3.19.0中禁用外键可以使UPDATE成功。

sqlite> .version
SQLite 3.19.0 2017-05-22 13:58:13 28a94eb282822cad1d1420f2dad6bf65e4b8b9062eda4a0b9ee8270b2c608e40
sqlite> .dump t2
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE T2 (Id INTEGER PRIMARY KEY AUTOINCREMENT , ParentId INTEGER NOT NULL , Name TEXT NOT NULL , FavoriteState INT NOT NULL DEFAULT 0, FOREIGN KEY (ParentId) REFERENCES T2(Id) );
INSERT INTO T2 VALUES(1,0,'/',2);
CREATE INDEX idx_pid_t2 ON T2 (ParentId);
CREATE INDEX idx_files_favstate_t2 on T2 (FavoriteState);
CREATE UNIQUE INDEX idx_pnc_t2 ON T2 (ParentId, Name);
COMMIT;
sqlite> PRAGMA foreign_keys=ON;
sqlite> WITH RECURSIVE under_favorite_path(parent) AS ( VALUES(1) UNION ALL SELECT T2.Id FROM T2 JOIN under_favorite_path ON T2.ParentId = under_favorite_path.parent ) UPDATE T2 SET FavoriteState = 1
WHERE Id IN under_favorite_path;
Error: FOREIGN KEY constraint failed
sqlite> PRAGMA foreign_keys=OFF;
sqlite> WITH RECURSIVE under_favorite_path(parent) AS ( VALUES(1) UNION ALL SELECT T2.Id FROM T2 JOIN under_favorite_path ON T2.ParentId = under_favorite_path.parent ) UPDATE T2 SET FavoriteState = 1
WHERE Id IN under_favorite_path;
sqlite> select * from t2;
1|0|/|1


这是我们的CTE的一个问题,该问题以前应该被标记吗?还是在更高版本的SQLite中可能已经退缩了?

最佳答案

问题不在于CTE,而是表包含无效数据(0不是有效ID):

sqlite> pragma foreign_key_check;
table       rowid       parent      fkid
----------  ----------  ----------  ----------
T2          1           T2          0

When the row gets modified in any way, the constraint is checked:

sqlite> update t2 set name = name;
Error: FOREIGN KEY constraint failed


显然,当未修改FK列时,旧版本未检查约束。

关于sqlite - SQLite递归CTE UPDATE在3.18.0中运行,SQL_CONSTRAINT在3.19.0中运行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46105601/

相关文章:

php - Android 中从 MySql 到 SQLite 的 JSON 数据

iOS:如何使用 SQLite.swift 将第一个运行的应用程序中的预置数据库复制到可写位置?

当表具有指向自身的链接时的 SQL 查询

mysql - 如何在 mySQL 中执行 "with CTE"?

android - 从简单的游标适配器填充微调器

c++ - SQLite - 从源代码构建库或将源代码直接包含到我的项目中

json - 使用 FOR JSON 和 CTE 并将其存储在变量中

sql - 在 postgresql 9.5 中是否可以从 WITH 查询中插入冲突更新?

sql - 何时使用公共(public)表表达式 (CTE)

android - 未知错误(代码 14): Could not open database