【问题标题】:How to filter a .mdb Access Database using TAdoTable.Filter如何使用 TAdoTable.Filter 过滤 .mdb 访问数据库
【发布时间】:2021-10-10 17:07:57
【问题描述】:

我有一个TADOConnection 和一个TADOTable,我想过滤表格中包含或以用户在表单上输入TEdit 的字符开头的职业。这是我目前使用的代码:

procedure TfrmProfessions.Filter;
begin
  if edtSearch.text = '' then
  begin
    dmPAT.ADOTable1.filtered := false;
  end
  else
  begin
    with dmPAT.ADOTable1 do
    begin
      filtered := false;
      Filter := 'Profession LIKE ' + quotedstr(edtSearch.text + '%');
      filtered := true;
    end;
  end;
end;

我在这一行遇到访问冲突,我不知道如何解决:

dmPAT.ADOTable1.filtered := false;

我怀疑这是因为,在表单的 OnCreate 事件中,我清除了 TEdit 并在创建 TADOTable 对象之前调用了 Filter procedure,尽管我不确定。

我遇到的另一个错误是Cannot convert type(Null) to type(String)。当我设置过滤器时,我得到了这个,它过滤了,但是没有与过滤器匹配的记录。我该如何解决?

【问题讨论】:

  • 1) 我强烈建议你放弃使用with 的习惯。您可能节省的最少打字时间不太可能弥补您以后可能花费的时间,以寻找因使用with 引起的模糊错误。 2) 您的 dmPat 是在您的 TfrmProfessions 之前自动创建的吗?如果不是,请确保它是。
  • @MartynA 我该怎么做?另外,当有空记录时,如何解决过滤器的问题?
  • 对于自动创建订单,请转到查看 |工具窗口 |IDE 中的项目,它应该向您显示包含数据模块和表单的单元。将数据模块单元小心拖到另一个上方。看看你的错误在哪里。接下来,您是否为您的表创建了持久 TFields? (通过右键单击它并从弹出窗口中选择Fields editor
  • 干得好。是的,第二个错误是异常而不是 AV AFAIK,它不应该发生,我觉得它可能是特定于访问的。我通常不使用 Access,但我会看看我是否可以在一夜之间设置一个测试 mdb。稍后...
  • 在我忘记之前,除此之外,如果您希望过滤器匹配包含用户输入内容的字段,您需要在 edtSEarchText 之前和之后使用 % 通配符

标签: delphi ms-access-2010


【解决方案1】:

尝试以下方法:

在您的程序 TfrmProfessions.Filter 的开头,添加这些行

Caption := dmPat.AdoTable1.FieldByName('Profession').ClassName;  // the purpose of this is 
//  so that you can tell at a glance what field type your Professions field is. which could be important

Assert(Assigned(dmPat));  //  Assert generates an exception if its argument is false
//  so this checks that dmPat has been created
Assert(Assigned(dmPat.AdoTable1));  //  checks that dmPat.AdoTable1 has been created.
Assert(dmPat.AdoTable1.Active);  // checks that the table is open

这应该可以解决没有创建 db 对象的任何问题。

更新我从您的 cmets 得知您已经解决了您的异常 问题,并期待阅读您是如何做到的。同时,我一直 编写以下内容并决定发布它,因为它可能会给您和其他人一些见解 了解如何调查您报告的问题。

我在 D10.4.2 中启动了一个新的 VCL,并在主窗体中添加了一个 TAdoConnection、一个 TAdoTable、一个 TADoCommand、一个 TDataSource 连接到 AdoTable1,连接到 DataSource 的 TDBGrid 和 TDBNavigator 以及用于搜索文本的 TEdit, 加上几个 TButtons 来调用下面的例程。我没有打扰 将 TAdoTable 等放入 TDataModule 中,因为我已经提到了如何处理它。

然后我创建了一个测试 Access db,其中包含一个名为 AName 的文本字段和一个名为 ANumber 的数字字段,使用 以下代码

procedure TForm1.Button3Click(Sender: TObject);
begin
  if AdoTable1.Active then
    AdoTable1.Close;
  AdoCommand1.CommandText :=

{.$define DropTable}
{$ifdef DropTable}
  'Drop table MATest';
{$else}
  'CREATE TABLE MATest (AName Char(12) , ANumber NUMBER)';
{$endif}

  AdoCommand1.Execute;
end;

$ifdef 让我可以轻松放下桌子重新开始。

互联网上的“接受的智慧” groups 似乎是要在 Delphi 代码中创建 Access 表,您需要使用 AdoX ActiveX 库,它独立于 Ado 访问组件。然而,TAdoCommand 完全有能力做到这一点。

然后,我使用以下过程向其中添加了一些记录:

procedure TForm1.InsertData;
begin
  ADoTable1.Open;

  AdoTable1.InsertRecord(['aaa']);
  AdoTable1.InsertRecord(['abb']);
  AdoTable1.InsertRecord(['bbb']);
end;

请注意,这故意不为第二个字段 ANumber 指定值,因此在数据库文件中,ANumber 字段应该接收 Access 用于缺失值的任何值(我希望 为 Null,但无论如何。

顺便说一句,在调查问题时,我总是在代码中创建新记录,以便 值在程序的每次运行中都保持不变,这样我就不用思考要输入的数据了。

然后我为 AdoTable1 添加了这个 AfterScroll 事件

procedure TForm1.ADOTable1AfterScroll(DataSet: TDataSet);
var
  F : TField;
  S : String;
begin
  F := AdoTable1.Fields[0];
  S := F.AsString;
  F := AdoTable1.Fields[1];
  S := S + ' / ' + F.AsString;
  Caption := S;
end;

这样做的目的是强制重新评估两者的字符串表示 字段以查看在表格中滚动是否引发了您遇到的异常。 结果:没有遇到异常。

然后我添加了以下代码来设置过滤

procedure TForm1.edtSearchChange(Sender: TObject);
begin
  UpdateFilter;
end;

procedure TForm1.UpdateFilter;
begin
  Assert(AdoTable1.Active);  // checks that the table is open
  AdoTable1.Filtered := False;  //  we should turn filtering off whether or not
  //  edSearchText.Text is blank or not

  if Trim(edtSearch.Text) <> '' then begin  //  Trim() removes leading and trailing blanks
    AdoTable1.Filter := 'AName LIKE ' + quotedstr('%' + edtSearch.Text + '%');
    AdoTable1.Filtered := True;
  end;
end;

而且效果很好。 aa 的过滤器匹配前两行,b 匹配第二行和第三行。 ADO Filter 属性记录在 here

QED。我希望这个示例表明,通过逐步构建测试项目而不是尝试调试已完成的项目,可以更轻松地调查您遇到的问题。

调查这一切后,我注意到前段时间注意到的关于 ADO 的一些事情:Delphi TAdo 组件通过 Windows 中的 MDAC(Microsoft 数据访问组件)层访问数据库,并且在某些数据库操作失败后会产生异常MDAC 层,在重新启动 Windows 之前,该层的行为异常。我很确定我在这里遇到了这个问题,因为在我尝试成功过滤后,发生了一些错误或其他错误,之后我根本无法让 Ado 过滤工作(它总是产生空结果)直到 我重新启动了 Windows。之后,它又恢复正常工作了。

更新 2

有一个挥之不去的问题。为什么如果 TDBEdit 连接到我的 ANumber 字段(或您的 Hours 字段),它工作正常,但如果我们转移字段值 手动启动 TEdit 或 TSpinEdit,我们得到字符串转换异常?我明白了 用这段代码

procedure TForm1.DataSource1DataChange(Sender: TObject; Field: TField);
begin
  sedNumber.Text := AdoTable1.FieldByName('ANumber').Value;
end;

答案在Vcl.DBCtrls单元的TDBedit源代码中找到:

procedure TDBEdit.DataChange(Sender: TObject);
begin
  if FDataLink.Field <> nil then
  begin
    [...]
    if FFocused and FDataLink.CanModify then
      Text := FDataLink.Field.Text
    else
    begin
      EditText := FDataLink.Field.DisplayText;
      if FDataLink.Editing and FDataLink.FModified then
        Modified := True;
    end;
  end [...]

请注意,这不会访问该字段的 Value,而是访问其 TextDisplayText 属性 这就是答案。所以一个比你的更简洁的解决方法就是简单地做

procedure TForm1.DataSource1DataChange(Sender: TObject; Field: TField);
begin
  sedNumber.Text := AdoTable1.FieldByName('ANumber').Text;
end;

这会在没有任何明确的 Null 检查的情况下使异常静音。它之所以有效是因为

procedure TFloatField.GetText

当字段包含 Null 时,在 Data.DB 中显式返回一个空字符串作为 Text 值。

【讨论】:

  • 再次感谢您的帮助!我会记下上面的代码。我发现delphi Could not convert type(Null) to type(string) 有一个相当简单和愚蠢的错误。放学后几个小时我会在下面分享我的答案。
  • 很高兴您找到它,请稍后分享。
【解决方案2】:

所以我追踪了这个问题。在我的DataModuleOnDataChange 事件中,我调用了这个过程:

procedure TdmPAT.DataSource1DataChange(Sender: TObject; Field: TField);
begin
  frmProfessions.Show_Record_Values;
end;

这是DBGrid表格中的程序:

procedure TfrmProfessions.Show_Record_Values;
begin
  with dmPAT.ADOTable1 do
  begin
    sedPK.value := FieldByName('PK').value;
    sedHours.value := FieldByName('Hours').value;

    edtProfession.text := fields[1].value;
    edtCost.text := floattostrf(FieldByName('Cost').AsFloat, ffcurrency, 12, 2);

    if FieldByName('Popular').asboolean then
      rgpPopular.ItemIndex := 0
    else
      rgpPopular.ItemIndex := 1;
  end;
end;

现在问题来了,一旦我过滤了DBGrid。这不是过滤器本身,但它会导致显示带有 Null 值的记录。然后当我尝试在编辑中显示活动记录(空记录)时,delphi 无法在以下行将 Null 转换为字符串:sedHours.value:=FieldByName('Hours').value;

这就是我得到错误的原因。 XD 一些 try 子句应该可以解决这个问题。 我已经重做了上述程序的代码,它解决了问题

procedure TfrmProfessions.Show_Record_Values;
var
  Profession, Hours, Cost, Popular: Tfield;
begin
  Profession := dmPAT.ADOTable1.fields[1];
  Hours := dmPAT.ADOTable1.fields[2];
  Cost := dmPAT.ADOTable1.fields[3];
  Popular := dmPAT.ADOTable1.fields[4];

  // check if null values
  if Profession.value = Null then
    edtProfession.text := ''
  else
    edtProfession.text := Profession.value;

  if Hours.value = Null then //The Hours Field is an Integer.
    sedHours.value := 0 //sedHours is a TSpinEdit.
  else
    sedHours.value := Hours.value;

  if Cost.value = Null then //The Cost field is Currency in MSAccess.
    edtCost.text := floattostrf(0, ffcurrency, 12, 2)
  else
    edtCost.text := floattostrf(Cost.value, ffcurrency, 12, 2);

  if Popular.value = Null then
  begin
    rgpPopular.ItemIndex := -1;
  end
  else
  begin
    case Popular.asboolean of
      true:
        rgpPopular.ItemIndex := 0;
      false:
        rgpPopular.ItemIndex := 1;
    end;
  end;
end;

【讨论】:

  • @MartynA,我不知道为什么我没有早点想到它。是的,with 子句很危险,但显然,我们使用它们会得到分数。
  • 很高兴您自己解决了这个问题。只有一件事:如果您说出您的 sedHours 及其 Value 属性是什么数据类型,我认为您的答案会更清楚。
  • sedHours 是 TSpinEditHours 字段是 integersed 是我们被指示在学校使用的缩写。
  • 谢谢。请参阅我的答案的更新 2,它整理了关于字符串转换异常的松散结尾。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-18
  • 1970-01-01
  • 2022-12-06
  • 2011-03-20
  • 1970-01-01
  • 2022-01-14
  • 1970-01-01
相关资源
最近更新 更多