2012-07-27 41 views
0

我想我错过了关于使用SQL语句和(Delphi的ADO)查询组件和/或在(Access 2003)数据库中的字段之间设置关系的基础知识。每当我想删除,更新等任何比SQL.Text =“SELECT something from aTable”更复杂的东西时,我会收到错误消息。如何删除,更新等由Delphi ADO查询生成的表格?

例如,我在表之间创建了一个简单的多对多关系,称为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上点击Delete按钮,(4)删除注释表中与所选记录具有相同NoteID和NoteText的记录。

+0

您是否在使用TADOQuery,以及“从此查询中删除记录”是什么意思? – 2012-07-27 16:46:38

+0

是的,TADOQuery。通过“删除记录......”我可以看到你的困惑;感谢您指出了这一点。看我的编辑。 – 2012-07-27 16:59:31

+0

当我使用TADOQuery加入sql&显示在dbgrid中时,我没有使用查询本身来删除记录。我使用带参数的TADOCommand来执行删除操作。我使用dbgrid.datasource.dataset.fieldbyname('param')。asString(或任何字段类型)的值为参数赋值。之后,我执行命令并刷新网格。 – Hendra 2012-07-28 04:16:09

回答

2

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正常工作。中止对于防止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; 
+0

关于保存光标位置,我没有试过这个,但是这个工作?如何获取当前记录的书签之前删除,做上面的代码,转到保存的书签(将去下一个记录),然后释放书签和中止?我不知道访问数据库是否是单向的。 – Hendra 2012-07-29 03:00:00

+0

@ Hendrea。您关于捕获当前记录的书签并在删除后恢复它的想法直到它被删除的最后一条记录。使用D2007,我无法避免TBookmark的异常,即使在TADOQuery.BookValid中尝试..除外。我有TBookmarkStr工作,但不喜欢使其工作的一些必要步骤(在我的有限测试中)。我已经用代码更新了我的答案。 至于你评论关于访问是单向的,这是TADQuery而不是数据库的功能。 – crefird 2012-07-29 18:48:03

+0

我想到了避免第二次询问。如果它不是最后一个记录,那么只能进入书签?因此,在执行上面的sql之前,请先检查最后一条记录(即dataset.next;如果dataset.eof然后设置isLastRecord = true; dataset.prior;)。重新查询后,替换尝试...除非用isLastRecord然后Dataset.Bookmark:= bm else Dataset.last;所以,第二个查询是不需要的。 – Hendra 2012-07-30 05:31:16

0

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

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

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

+0

L - 我尝试了你的建议,通过我已经使用的ADOConnection组件执行DELETE语句。我收到一个错误,指出无法找到行进行更新,该值自上次读取后可能已更改。有趣的是,该记录仍然被删除 - 但其他表格中的记录也是如此。 – 2012-07-27 18:12:15

+0

听起来像原来的'TADOQuery'试图从数据库中删除它,你手动删除后。也许只是在'BeforeDelete'事件中做'TADOQuery.Delete'而不做任何事情就足够了?我不经常使用ADO组件 - 如果查询不太复杂,也许他们处理更新/删除操作......也许'TADOQuery'正在删除所有参与表中的记录?如果你只是想删除* 1 *记录,你需要找到一种方法来关闭原来的'TADOQuery'的自动更新...并在'BeforeDelete'中手动处理... – 2012-07-27 18:20:00