同时选择和插入时sqlite和'约束失败'错误

时间:2011-03-16 08:27:58

标签: sql sqlite

我正在研究迁移功能。它从旧表中读取数据并将其插入新表中。所有那些在后台线程中工作的东西都是低优先级的。

我在伪代码中的步骤。

sqlite3_prepare_stmt (select statement)
sqlite3_prepare_stmt (insert statement)

while (sqlite3_step (select statement) == SQLITE_ROW)
{
    get data from select row results
    sqlite3_bind select results to insert statement
    sqlite3_step (insert statement)
    sqlite3_reset (insert statement)
}

sqlite3_reset (select statement)

我总是在sqlite3_step (insert statement)上遇到“约束失败”错误。为什么会发生这种情况以及如何解决这个问题?

UPD:因为我知道发生这种情况是因为后台线程使用在主线程中打开的db句柄。现在检查猜测。

UPD2:

sqlite> select sql from sqlite_master where tbl_name = 'tiles';
CREATE TABLE tiles('pk' INTEGER PRIMARY KEY, 'data' BLOB, 'x' INTEGER, 'y' INTEGER, 'z' INTEGER, 'importKey' INTEGER)
sqlite> select sql from sqlite_master where tbl_name = 'tiles_v2';
CREATE TABLE tiles_v2 (pk int primary key, x int, y int, z int, layer int, data blob, timestamp real)

6 个答案:

答案 0 :(得分:9)

这可能意味着您的insert语句违反了新表中的约束。可以是主键约束,唯一约束,外键约束(如果您使用PRAGMA foreign_keys = ON;),等等。

您可以通过删除约束,更正数据或删除数据来解决此问题。删除约束通常是一件坏事,但这取决于应用程序。

是否有令人信服的理由一次一行地复制数据而不是作为一组?

INSERT INTO new_table
SELECT column_list FROM old_table;

如果您需要帮助来识别约束,请编辑原始问题,并发布这两个SQLite查询的输出。

select sql from sqlite_master where tbl_name = 'old_table_name';
select sql from sqlite_master where tbl_name = 'new_table_name';

更新:根据这两个查询的输出,我只看到一个约束 - 每个表中的主键约束。如果尚未在这些表上构建任何触发器,则唯一可能失败的约束是主键约束。约束失败的唯一方法是,如果您尝试插入两行具有相同“pk”值的行。

我想这可能会以几种不同的方式发生。

  • 旧表中包含重复值 'pk'栏。
  • 执行迁移的代码 改变或注入重复值 在将数据插入新的数据之前 表
  • 另一个可能正在运行的进程 一台不同的电脑,插入或 在您不知情的情况下更新数据。
  • 我还没有想到的其他原因。 : - )

您可以通过运行此查询来确定旧表中是否存在重复的“pk”值。

select pk 
from old_table_name
group by pk
having count() > 1;

您可能会考虑尝试使用INSERT INTO . . . SELECT . . .手动迁移数据如果失败,请添加WHERE子句以减小集的大小,直到您隔离坏数据为止。

答案 1 :(得分:2)

万一有人在这里寻找"约束失败"错误消息,请确保您的Id列的类型为INTEGER,而不是INTEGER (0, 15)或其他类型。

<强>背景

如果您的表中有一个名为Id的列,其类型为INTEGER并设置为主键,则SQLite会将其视为内置列RowId的别名。此列的作用类似于自动增量列。在我的情况下,这个专栏工作正常,直到一些表设计器(可能是SQLite人为Visual Studio创建的那个)将列类型从INTEGER更改为INTEGER (0, 15)并且突然我的应用程序开始抛出{ {1}}例外。

答案 2 :(得分:1)

只是为了让它更清晰:
确保您使用的数据类型正确,我使用int代替integer来创建这样的tmy表:

id int primary key not null

我长时间在约束问题上卡住 ...... 只需确保在创建数据库时正确输入数据类型。

答案 3 :(得分:0)

发生此错误的原因之一是也为唯一行插入重复数据。检查所有唯一和密钥是否有重复数据。

答案 4 :(得分:0)

我发现用sqlite排除外键约束错误很麻烦,尤其是在大型数据集上。但是,以下方法可帮助您确定有问题的关系。

  1. 禁用外键检查:PRAGMA foreign_keys = 0;
  2. 执行导致错误的语句-在我的情况下,这是一个INSERT,包含70,000行,具有3种不同的外键关系。
  3. 重新启用外键检查:PRAGMA foreign_keys = 1;
  4. 识别外键错误:PRAGMA foreign_key_check(table-name);

就我而言,它显示了13行带有无效引用。

答案 5 :(得分:0)

今天我遇到了类似的错误:CHECK constraint failed: profile

首先我读了 here 什么是约束。

<块引用>

CONSTRAINTS 是一个完整性,它定义了一些条件 插入或插入时限制列包含真实数据 更新或删除。我们可以使用两种类型的约束,即 列级或表级约束。列级约束 只能应用于表级别的特定列 约束可以应用于整个表。

然后发现我需要检查数据库是如何创建的,因为我使用了 sqlalchemy 和 sqlite3 方言,我不得不检查表 schema。 Schema 将显示实际的数据库结构。

sqlite3 >>> .schema
CREATE TABLE profile (
    id INTEGER NOT NULL, 
    ...
    forum_copy_exist BOOLEAN DEFAULT 'false', 
    forum_deleted BOOLEAN DEFAULT 'true', 
    PRIMARY KEY (id), 
    UNIQUE (id), 
    UNIQUE (login_name), 
    UNIQUE (forum_name), 
    CHECK (email_verified IN (0, 1)), 
    UNIQUE (uid), 
    CHECK (forum_copy_exist IN (0, 1)), 
    CHECK (forum_deleted IN (0, 1))

所以在这里我发现布尔值 CHECK 总是 0 或 1 并且我使用列默认值作为“false”,所以每次我没有 forum_copy_exist 或 forum_deleted 值时,插入了 false,因为 false 是无效值,它会抛出错误, 并且没有插入行。

因此更改数据库默认为:

forum_copy_exist BOOLEAN DEFAULT '0', 
forum_deleted BOOLEAN DEFAULT '0',

解决了这个问题。

在 postgresql 中,我认为 false 是有效值。所以这取决于数据库架构是如何创建的。

希望这会在未来对其他人有所帮助。