【问题标题】:How do you delete, update, etc. tables produced by queries in Delphi ADO?在 Delphi ADO 中如何删除、更新等查询产生的表?
【发布时间】:2012-07-27 16:18:58
【问题描述】:

我认为我缺少有关使用 SQL 语句和(Delphi 的 ADO)查询组件和/或设置(Access 2003)数据库中的字段之间的关系的一些基本知识。每当我想删除、更新等比 SQL.Text="SELECT something FROM aTable." 更复杂的内容时,我都会收到错误消息。

例如,我在名为 Outline 和 Reference 的表之间创建了一个简单的多对多关系。连接或连接表称为注意:

Outline
  OutlineID (PK)
  etc.

Reference
  RefID (PK)
  etc.

Note
  NoteID (PK)
  OutlineID
  RefID
  NoteText

我在 Access 中的联接上强制执行参照完整性,但没有勾选复选框以级联删除或更新。同时,在 Delphi 中,我的 Query.SQL.Text 是

SELECT Note.NoteID, Outline.OutlineID, Ref.RefID, Note.NoteText, Ref.Citation, Outline.OutlineText
FROM (Note LEFT JOIN Outline ON Outline.OutlineID=Note.OutlineID)
LEFT JOIN Ref on Ref.RefID=Note.RefID; 

最初我在 SELECT 语句中省略了对键的引用,当我尝试从结果表中删除记录时产生“键列信息不足”错误。我认为我理解:您必须选择数据库需要执行的任何操作的所有字段。如果它不知道加入了什么,它就不能删除、更新等加入的字段。 (对吗?)

那么,我该如何从这个查询中删除一条记录呢?换句话说,我想 (1) 显示一个显示 NoteText、Citation 和 OutlineText 的网格,(2) 从网格中选择一条记录,(3) 执行一些操作,例如单击 DBNavigator 上的删除按钮,以及 (4) 删除Note 表中与所选记录具有相同 NoteID 和 NoteText 的记录。

【问题讨论】:

  • 您在使用 TADOQuery 吗?“从此查询中删除记录”是什么意思?
  • 是的,TADOQuery。通过“删除记录……”我可以看出您的困惑;感谢您指出了这一点。查看我的编辑。
  • 当我在 dbgrid 中显示的 sql 和连接中使用 TADOQuery 时,我没有使用查询本身来删除记录。我使用带有参数的 TADOCommand 进行删除。我为参数分配了来自 dbgrid.datasource.dataset.fieldbyname('param').asString (或任何字段类型)的值。之后我执行命令,并刷新网格。

标签: database delphi ado


【解决方案1】:

James L 和 Hendra 都提供了如何做你想做的事的精髓。以下是实现它的一种方法。

procedure TForm1.ADOQuery1BeforeDelete(DataSet: TDataSet);
var
  SQL : string;
begin
  SQL := 'DELETE FROM [Note] WHERE NoteID='+
    DataSet.FieldByName('NoteID').AsString;
  ADOConnection1.Execute(SQL);
  TADOQuery(DataSet).ReQuery;
  Abort;
end;

这将允许 TADOQuery.Delete 正常工作。 Abort 是必要的,以防止 TADOQuery 在您删除记录后也尝试删除它。主要缺点是 TADOQuery.ReQuery 不保留光标位置,即当前记录将是第一条记录。

更新:

以下尝试恢复光标。我不喜欢第二个 Requery,但在尝试恢复无效书签后似乎有必要恢复 DataSet(由于删除了最后一条记录)。这适用于我的有限测试。

procedure TForm1.ADOQuery1BeforeDelete(DataSet: TDataSet);
var
  SQL : string;
  bm : TBookmarkStr;
begin
  SQL := 'DELETE FROM [Note] WHERE NoteID='+
    DataSet.FieldByName('NoteID').AsString;
  bm := Dataset.BookMark;
  ADOConnection1.Execute(SQL);
  TADOQuery(DataSet).ReQuery;
  try
    Dataset.BookMark := bm;
  except
    TADOQuery(DataSet).Requery;
    DataSet.Last;
  end;
  Abort;
end;

【讨论】:

  • 关于保留光标位置,我还没有尝试过,但是这样可以吗?如何在删除之前获取当前记录的书签,执行上面的代码,转到保存的书签(将转到下一条记录),然后释放书签并中止?不知道access数据库是不是单向的。
  • @Hendrea。您关于捕获当前记录的书签然后在删除后恢复它的想法直到它是被删除的最后一条记录。使用 D2007,即使在 try .. except 中使用 TADOQuery.BookValid,我也无法避免 TBookmark 出现异常。我让 TBookmarkStr 工作,但不喜欢使其工作所需的一些步骤(在我有限的测试范围内)。我已经用代码更新了我的答案。至于您评论 Access 是单向的,那是 TADQuery 而不是数据库的功能。
  • 我知道如何避免第二次重新查询。如果它不是最后一条记录,只去书签怎么样?因此,在执行上面的 sql 之前,首先检查最后一条记录(即 dataset.next; if dataset.eof then set isLastRecord = true; dataset.prior;)。重新查询后,将 try...except 替换为 if not isLastRecord then Dataset.Bookmark := bm else Dataset.last;因此,不需要第二次重新查询。
  • 感谢您的回复@crefird。结合其他回复,这正是我正在寻找的那种信息。我将来可以使用它。 ... FWIW,我最终通过 ADODataSet 的“唯一表”属性解决了我最初的问题。请参阅stackoverflow.com/questions/7981718/… 了解更多信息。
  • @Hendra 我会试试你的建议。但我认为 Al C 使用“唯一表”属性找到了更好的解决方案。
【解决方案2】:

如果您使用的是TADOTable,则当您从TADOTable 数据集中删除它们时,组件会处理数据库中的删除。但是,由于您使用的是连接多个表的TADOQuery,因此您需要以不同的方式处理数据库删除。

当您创建要删除数据库网格中当前记录的记录时,它会将TADOQuery 的光标滚动到其数据集中的该行。然后您可以使用TADOQuery.Delete 删除当前记录。如果您为TADOQuery.BeforeDelete事件编写代码,您可以在本地删除之前从记录中捕获id字段,并使用另一个TADOQueryTADOCommand组件,您可以创建并执行删除记录的SQL (s) 来自数据库。

由于删除数据库记录的代码在BeforeDelete事件中,如果发生异常,数据库记录没有被删除,本地删除也会被取消,本地记录也不会被删除-- 并且会显示错误(例如,'外键违规'...)。

【讨论】:

  • L -- 我尝试了您的建议,通过我已经在使用的 ADOConnection 组件执行 DELETE 语句。我收到一条错误消息,指出无法找到用于更新的行,该值可能自上次读取以来已更改。有趣的是,该记录仍然被删除了——但其他表之一中的记录也是如此。
  • 听起来原来的TADOQuery 也在尝试从数据库中删除它,在您手动执行删除之后。也许只做TADOQuery.Delete 而不在BeforeDelete 事件中做任何事情就足够了?我不经常使用 ADO 组件——如果查询不太复杂,它们可能会处理更新/删除......也许TADOQuery 正在从所有参与表中删除记录?如果您只想删除 one 记录,则需要找到一种方法来关闭原始 TADOQuery... 的自动更新并在 BeforeDelete... 中手动处理它...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多