sqlite 和“约束失败”同时选择和插入时出错

发布于 2024-10-21 22:52:46 字数 1004 浏览 1 评论 0原文

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

我的伪代码步骤。

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(插入语句) 上收到“约束失败”错误。为什么会发生这种情况以及我该如何解决这个问题?

UPD:据我了解,发生这种情况是因为后台线程使用主线程中打开的数据库句柄。现在检查这个猜测。

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)

I'm working on migration function. It reads data from old table and inserts it into the new one. All that stuff working in background thread with low priority.

My steps in pseudo code.

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)

I'm always getting 'constraint failed' error on sqlite3_step (insert statement). Why it's happend and how i could fix that?

UPD: As i'm understand that's happend because background thread use db handle opened in main thread. Checking that guess now.

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)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

分开我的手 2024-10-28 22:52:46

这可能意味着您的插入语句违反了新表中的约束。可能是主键约束、唯一约束、外键约束(如果您使用的是PRAGMAforeign_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 手动迁移数据。 。 。选择 。 。 . 如果失败,请添加 WHERE 子句来减小集合的大小,直到隔离出错误数据。

It probably means your insert statement is violating a constraint in the new table. Could be a primary key constraint, a unique constraint, a foreign key constraint (if you're using PRAGMA foreign_keys = ON;), and so on.

You fix that either by dropping the constraint, correcting the data, or dropping the data. Dropping the constraint is usually a Bad Thing, but that depends on the application.

Is there a compelling reason to copy data one row at a time instead of as a set?

INSERT INTO new_table
SELECT column_list FROM old_table;

If you need help identifying the constraint, edit your original question, and post the output of these two SQLite queries.

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

Update: Based on the output of those two queries, I see only one constraint--the primary key constraint in each table. If you haven't built any triggers on these tables, the only constraint that can fail is the primary key constraint. And the only way that constraint can fail is if you try to insert two rows that have the same value for 'pk'.

I suppose that could happen in a few different ways.

  • The old table has duplicate values in
    the 'pk' column.
  • The code that does your migration
    alters or injects a duplicate value
    before inserting data into your new
    table.
  • Another process, possibly running on
    a different computer, is inserting or
    updating data without your knowledge.
  • Other reasons I haven't thought of yet. :-)

You can determine whether there are duplicate values of 'pk' in the old table by running this query.

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

You might consider trying to manually migrate the data using INSERT INTO . . . SELECT . . . If that fails, add a WHERE clause to reduce the size of the set until you isolate the bad data.

淡笑忘祈一世凡恋 2024-10-28 22:52:46

我发现使用 sqlite 排除外键约束错误很麻烦,特别是在大型数据集上。然而,以下方法有助于识别违规关系。

  1. 禁用外键检查:PRAGMAforeign_keys = 0;
  2. 执行导致错误的语句 - 在我的例子中,它是具有 3 个不同外键关系的 70,000 行的 INSERT。
  3. 重新启用外键检查:PRAGMAforeign_keys = 1;
  4. 识别外键错误:PRAGMAforeign_key_check(table-name);

在我的例子中,它显示了13行无效的参考文献。

I have found that troubleshooting foreign key constraint errors with sqlite to be troublesome, particularly on large data sets. However the following approach helps identify the offending relation.

  1. disable foreign key checking: PRAGMA foreign_keys = 0;
  2. execute the statement that cause the error - in my case it was an INSERT of 70,000 rows with 3 different foreign key relations.
  3. re-enable foreign key checking: PRAGMA foreign_keys = 1;
  4. identify the foreign key errors: PRAGMA foreign_key_check(table-name);

In my case, it showed the 13 rows with invalid references.

清音悠歌 2024-10-28 22:52:46

以防万一有人登陆此处寻找“约束失败”错误消息,请确保您的 Id 列的类型为 INTEGER,而不是 INTEGER (0, 15)代码>或其他东西。

背景

如果您的表有一个名为 Id 的列,类型为 INTEGER 并设置为主键,SQLite 会将其视为内置的别名。在 RowId 列中。该列的工作原理类似于自动增量列。就我而言,该列工作正常,直到某个表设计者(可能是 SQLite 人员为 Visual Studio 创建的表设计者)将列类型从 INTEGER 更改为 INTEGER (0, 15) 突然我的应用程序开始抛出约束失败异常。

Just in case anyone lands here looking for "Constraint failed" error message, make sure your Id column's type is INTEGER, not INTEGER (0, 15) or something.

Background

If your table has a column named Id, with type INTEGER and set as Primary Key, SQLite treats it as an alias for the built-in column RowId. This column works like an auto-increment column. In my case, this column was working fine till some table designer (probably the one created by SQLite guys for Visual Studio) changed column type from INTEGER to INTEGER (0, 15) and all of a sudden my application started throwing Constraint failed exception.

疯狂的代价 2024-10-28 22:52:46

只是为了让它更清楚:

确保您使用的数据类型正确,我在创建 tmy 表时使用的是 int 而不是 integer ,如下所示:

id int 主键不为 null< /code>

并且我在几个小时内陷入了约束问题......
只需确保在创建数据库时正确输入数据类型即可。

just for making it more clearer :

make sure that you use data type correct , i was using int instead of integer on creating tmy tables like this :

id int primary key not null

and i was stuck on the constrain problem over hours ...
just make sure that to enter data type correctly in creating database.

二手情话 2024-10-28 22:52:46

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

One of the reasons that this error occurs is inserting duplicated data for a unique row too. Check your all unique and keys for duplicated data.

眼趣 2024-10-28 22:52:46

今天我遇到了类似的错误:检查约束失败:配置文件

首先我读了这里什么是约束。

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

然后发现我需要检查数据库是如何创建的,因为我使用了 sqlalchemy 和 sqlite3 方言,所以我必须检查表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 是有效值。所以这取决于数据库模式是如何创建的。

希望这将来能对其他人有所帮助。

Today i had similar error: CHECK constraint failed: profile

First i read here what is constraint.

The CONSTRAINTS are an integrity which defines some conditions that
restrict the column to contain the true data while inserting or
updating or deleting. We can use two types of constraints, that is
column level or table level constraint. The column level constraints
can be applied only on a specific column where as table level
constraints can be applied to the whole table.

Then figured out that i need to check how database was created, because i used sqlalchemy and sqlite3 dialect, i had to check tables schema. Schema will show actual database structure.

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

So here I found that boolean CHECK is always 0 or 1 and i used column default value as 'false', so each time i had no forum_copy_exist or forum_deleted value, false was inserted, and because false is invalid value it throws error, and didn't inserted row.

So changing database defaults to:

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

solved the issue.

In postgresql i think false is valid value. So it depends how database schema is created.

Hope this will help others in the future.

时光无声 2024-10-28 22:52:46

我在引用表上使用INTEGER作为INT,并在引用表中使用INTEGER作为外键。问题是你需要在这两个地方使用 INTEGER 。保持一致。因此在这两种情况下使用 INTEGER 解决了我的问题。 现在它运行起来就像魔法一样。

I was using INTEGER as INT on the referencing table, and INTEGER in the referenced table for the foreign key. The thing is that you need to use INTEGER in both places. Be consistent. So using INTEGER in both cases, solved my problem. Now it runs like magic.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文