【发布时间】:2016-10-07 03:27:07
【问题描述】:
当 delta 包含对数据库具有 UNIQUE 约束的字段时,我无法解决缓存更新问题。我有一个具有以下 DDL 架构的数据库(内存中的 SQLite 可用于重现):
create table FOO
(
ID integer primary key,
DESC char(2) UNIQUE
);
初始数据库表包含一条 ID = 1 且 DESC = R1 的记录
使用 TFDQuery(从 FOO 中选择 *)访问此表,如果执行以下步骤,生成的 delta 将通过 ApplyUpdates 正确应用:
- 将记录 ID = 1 更新为 DESC = R2
- 使用 DESC = R1 附加新记录 ID = 2
Delta 包括以下内容:
- R2
- R1
ApplyUpdates 上不会产生错误,因为 delta 上的第一个操作将是更新。第二个将是插入。由于记录 1 现在是 R2,因此可以插入,因为没有违反此事务的唯一约束。
现在,执行以下步骤,将生成完全相同的增量(查看 FDQuery.Delta 属性),但将生成 UNIQUE 约束违规。
- 使用 DESC = TT 附加一个新的临时记录 ID = 2
- 将第一条记录 ID = 1 更新为 DESC = R2
- 将临时记录 2 - TT 更新为 DESC = R1
Delta 包括以下内容:
- R2
- R1
请注意,FireDAC 在两种情况下都会生成相同的 delta,这可以通过 FDquery 的 Delta 属性查看。
此步骤可用于重现错误:
文件 > 新的 VCL 表单应用程序;在表单上删除 FDConnection 和 FDQuery;将 FDConnection 设置为使用 SQLite 驱动程序(在内存数据库中使用);在表单上拖放两个按钮,一个用于重现正确的行为,另一个用于重现错误,如下所示:
按钮确定:
procedure TFrmMain.btnOkClick(Sender: TObject);
begin
// create the default database with a FOO table
con.Open();
con.ExecSQL('create table FOO' + '(ID integer primary key, DESC char(2) UNIQUE)');
// insert a default record
con.ExecSQL('insert into FOO values (1,''R1'')');
qry.CachedUpdates := true;
qry.Open('select * from FOO');
// update the first record to T2
qry.First();
qry.Edit();
qry.Fields[1].AsString := 'R2';
qry.Post();
// append the second record to T1
qry.Append();
qry.Fields[0].AsInteger := 2;
qry.Fields[1].AsString := 'R1';
qry.Post();
// apply will not generate a unique constraint violation
qry.ApplyUpdates();
end;
按钮错误:
// create the default database with a FOO table
con.Open();
con.ExecSQL('create table FOO' + '(ID integer primary key, DESC char(2) UNIQUE)');
// insert a default record
con.ExecSQL('insert into FOO values (1,''R1'')');
qry.CachedUpdates := true;
qry.Open('select * from FOO');
// append a temporary record (TT)
qry.Append();
qry.Fields[0].AsInteger := 2;
qry.Fields[1].AsString := 'TT';
qry.Post();
// update R1 to R2
qry.First();
qry.Edit();
qry.Fields[1].AsString := 'R2';
qry.Post();
qry.Next();
// update TT to R1
qry.Edit();
qry.Fields[1].AsString := 'R1';
qry.Post();
// apply will generate a unique contraint violation
qry.ApplyUpdates();
【问题讨论】:
-
我要回答的是,在事务中,您可以在更新之前删除唯一约束,并在 Applyupdates 之后重新启用它。但是貌似Sqlite不能去掉唯一约束(需要新建一个没有约束的新表,把所有数据都移到那里),所以这不是一个可行的方案。
-
欢迎来到 StackOverflow 和写得很好的问题。即使使用
CachedUpdates := False,我也可以在 D 西雅图复制此内容。与 Sql 脚本一样执行的步骤不会出错。有趣的是,在qrry.Next之前插入一个额外的ApplyUpdates后仍然会出现错误。
标签: database sqlite delphi unique firedac