ADOQuery AfterScroll 没有触发一条记录/没有记录

ADOQuery AfterScroll not Triggered with One Record / No Record

我在表单上有一个 MasterQry 和一个 SlaveQry。 MasterQry 是这样的:

select * from Header where Active = 1

在它的 AfterScroll 事件中,我有以下内容:

select * from Slave where HeadID=:Header.ID

现在如果我这样做:

if MasterQry.Active then MasterQry.Close;
MasterQry.Open;

如果我有不止一条记录,这将完美运行,但如果我只有一条,则无法运行。

即使我这样做MasterQry.First;也没有任何反应。

如果我尝试 MasterQry.AfterScroll(MasterQry) 我会遇到访问冲突。

我正在重构我的代码并试图使其更紧凑,因为我做了很多打开关闭定位 ID(需要刷新数据以获得实际状态,是否已锁定等)并且我这样做了:

function RefreshQuery(AQuery : TADOQuery; ID : integer) : boolean ; overload;
var AfterOpen,AfterScroll : TDataSetNotifyEvent;
begin
  result:=false;

  AfterOpen := AQuery.AfterOpen;
  AfterScroll := AQuery.AfterScroll;

  AQuery.AfterOpen:=nil;
  AQuery.AfterScroll:=nil;

  if AQuery.Active then AQuery.Close;
  AQuery.Open;

  if not AQuery.Locate('id', ID, []) then
    result:=false
  else
    result:=true;

  AQuery.AfterOpen:=AfterOpen;
  AQuery.AfterScroll:=AfterScroll;
  if Assigned(AQuery.AfterScroll) then
    AQuery.AfterScroll(AQuery);
end;

请注意此代码不是通用的,但非常适合我的需要。我注意到这里的 AfterScroll 事件被触发,即使我在 MasterQry 中只有一个记录,或者即使我根本没有。我真的很高兴,我多次测试它并提供了正确的结果。

我检查了 TADOQuery.First 和 TADOQuery.Locate 程序,它们都有 DoAfterScroll 但它不是由 One Record 或 No Record 触发的。 (SlaveQry 在不希望的状态下打开)

为此我在谷歌上搜索了很多,但找不到原因。

我的问题是:为什么这样做有效?为什么 AfterScroll 会以 One or No Record 触发。

谢谢。

更新

我只能用 Microsoft SQL 重现这个。所以为了测试这个你需要。两个表 .

MasterTable中添加两条记录(ID、Text、Active)

1 前 1

2秒1

SlaveTable中添加两条或多条记录(ID,HeadID,Text)

1,1,First-1

2,1,First-2

3,2,第二-1

4,2,第二-2

现在放在表格上 1 ADOConnection 2 ADOQueries .

MainQuery 中您有以下文本

Select * from MasterTable where Active=1

SlaveQuery 中你有以下文本

select * from SlaveTable where HeadID=:HeadID

MainQuery.BeforeOpen 你有这个 :

MainQuery.AfterScroll:=nil;

MainQuery.AfterScroll 你有这个:

if SlaveQuery.Active then SlaveQuery.Close;
SlaveQuery.Parameters.ParamByName('HeadID').Value:=MainQueryID.Value;
SlaveQuery.Open;

MainQuery.AfterOpen 你有这个 :

MainQuery.AfterScroll:=MainQueryAfterScroll;

向此表单添加一个 按钮 :

Button1Click 事件包含以下内容:

if MasterQuery.Active then MasterQuery.Close;
MasterQuery.Open;

因此,如果您现在将网格附加到两个查询,您可以看到它完美地遵循。

在不关闭程序的情况下,进入 SQL 服务器管理器并 运行 以下更新语句:

update MasterTable set Active=0

再次按下窗体上的 Button1 :

MasterQuery 为空,SlaveQuery 处于上次打开状态。

为了解决这个问题,您需要按如下方式更改 Button1Click :

var AfterOpen,AfterScroll : TDataSetNotifyEvent;
begin
  AfterOpen := AQuery.AfterOpen;
  AfterScroll := AQuery.AfterScroll;

  AQuery.AfterOpen:=nil;
  AQuery.AfterScroll:=nil;

  if AQuery.Active then AQuery.Close;
  AQuery.Open;

  AQuery.AfterOpen:=AfterOpen;
  AQuery.AfterScroll:=AfterScroll;
  if Assigned(AQuery.AfterScroll) then
    AQuery.AfterScroll(AQuery);
end;

现在可以了。我不知道为什么,因为 MasterQuery.First 应该触发 DoAfterScroll 但什么也没发生。 似乎将 AfterScroll 设置为 nil 然后再返回以某种方式触发 AfterScroll,即使它有 1 条记录或为空也是如此。

正如我在评论中所说,RefreshQuery 中的大部分代码都不是必需的, 因为链接 Master->Detail 数据集应该 "just work"。事实上,您的 RefreshQuery 根本不需要。

我创建了一个基于你的主从 table 的最小项目,只是通过删除组件 从托盘中,将它们连接起来并仅添加下面 Form1.FormCreate 中的代码。这 从网格的内容正确地跟踪主网格,包括以下情况 没有匹配的 Slave 记录,即 Slave 网格显示为空。请注意,没有任何需要的数据事件,即没有 AfterScrollLocate 等调用。

  type
    TForm1 = class(TForm)
      dsMaster: TDataSource;
      DBGrid1: TDBGrid;
      DBNavigator1: TDBNavigator;
      DBGrid2: TDBGrid;
      DataSource2: TDataSource;
      DBNavigator2: TDBNavigator;
      ADOConnection1: TADOConnection;
      qMaster: TADOQuery;
      qSlave: TADOQuery;
      qSlaveID: TIntegerField;
      qSlaveHeaderID: TIntegerField;
      qSlaveAText: TWideStringField;
      procedure FormCreate(Sender: TObject);
    public
    end;

  [...]

  procedure TForm1.FormCreate(Sender: TObject);
  begin
    qMaster.SQL.Text := 'select * from mastertable';

    qSlave.DataSource := dsMaster;
    qSlave.SQL.Text := 'select * from slavetable where headerid = :id';
    //  NOTE: because the DataSource property of qSlave is set to dsMaster,
    //  the ` = :id` tells the Ado run-time code to get the value of the
    //  ID field in the qMaster table.

    qMaster.Open;
    qSlave.Open;
  end;

如果你想刷新 Master 或 Slave table 以防其他用户更改其中的记录,你可以这样做:

procedure TForm1.Button1Click(Sender: TObject);
begin
  qMaster.Refresh;
end;

但请注意 table 需要在 Sql 服务器上正确设置。只要将 ID 字段设置为主键,and/or 在服务器上设置了唯一索引,对 Refresh 的调用应该可以正常工作,但如果没有,您将得到一条错误消息 "Insufficient key information for updating or refreshing." 您当然可以在计时器上进行刷新(但不要过于频繁地调用它,即每隔几秒多次)。