如何使用 TAdoTable.Filter 过滤 .mdb Access 数据库
How to filter a .mdb Access Database using TAdoTable.Filter
我有一个 TADOConnection
和一个 TADOTable
,我想过滤 table 以查找包含或以用户键入 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)
。当我设置过滤器时我得到了这个,它过滤了,但是没有匹配过滤器的记录。我该如何解决?
尝试以下操作:
在程序开始时 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
这么多应该可以解决所有与尚未创建的数据库对象有关的问题。
更新 我从你的评论中了解到你已经解决了你的异常
问题并期待阅读你是如何做到的。与此同时,我一直
编写以下内容并决定 post 它可能会给您和其他人一些见解
了解如何着手调查您报告的问题。
我在 D10.4.2 中启动了一个新的 VCL,并在主窗体中添加了一个 TAdoConnection、一个 TAdoTable、一个 TADoCommand、一个 TDataSource
连接到 AdoTable1,一个 TDBGrid 和 TDBNavigator 连接到 DataSource 和一个用于搜索文本的 TEdit,
加上几个 TButtons 来调用下面的例程。我没有打扰
将 TAdoTable 等放入 TDataModule 中,因为我已经提到了如何处理它。
然后我创建了一个测试 Access 数据库,其中包含一个名为 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
这样我就可以轻松地放下 table 并重新开始。
网络中的“后天智慧”
groups 似乎是要在 Delphi 代码中创建 Access table,您需要使用 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;
这样做的目的是强制重新评估两者的字符串表示形式
字段以查看在 table 周围滚动是否引发了您遇到的异常。
结果:没有遇到异常。
然后我添加了以下代码来设置过滤
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 字段),它工作正常,但如果我们 t运行sfer 字段值
手动输入 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
,而是它的 Text
或 DisplayText
属性
这就是答案。因此,一个比你的更简洁的修复方法就是简单地做
procedure TForm1.DataSource1DataChange(Sender: TObject; Field: TField);
begin
sedNumber.Text := AdoTable1.FieldByName('ANumber').Text;
end;
并且在没有任何显式 Null 检查的情况下使异常静音。它之所以有效,是因为
procedure TFloatField.GetText
在 Data.DB 中显式 returns 空字符串作为 Text
字段包含空值时的值。
所以我追查到了问题所在。在我的 DataModule
的 OnDataChange
事件中,我调用了这个过程:
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;
我有一个 TADOConnection
和一个 TADOTable
,我想过滤 table 以查找包含或以用户键入 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)
。当我设置过滤器时我得到了这个,它过滤了,但是没有匹配过滤器的记录。我该如何解决?
尝试以下操作:
在程序开始时 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
这么多应该可以解决所有与尚未创建的数据库对象有关的问题。
更新 我从你的评论中了解到你已经解决了你的异常 问题并期待阅读你是如何做到的。与此同时,我一直 编写以下内容并决定 post 它可能会给您和其他人一些见解 了解如何着手调查您报告的问题。
我在 D10.4.2 中启动了一个新的 VCL,并在主窗体中添加了一个 TAdoConnection、一个 TAdoTable、一个 TADoCommand、一个 TDataSource 连接到 AdoTable1,一个 TDBGrid 和 TDBNavigator 连接到 DataSource 和一个用于搜索文本的 TEdit, 加上几个 TButtons 来调用下面的例程。我没有打扰 将 TAdoTable 等放入 TDataModule 中,因为我已经提到了如何处理它。
然后我创建了一个测试 Access 数据库,其中包含一个名为 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
这样我就可以轻松地放下 table 并重新开始。
网络中的“后天智慧” groups 似乎是要在 Delphi 代码中创建 Access table,您需要使用 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;
这样做的目的是强制重新评估两者的字符串表示形式 字段以查看在 table 周围滚动是否引发了您遇到的异常。 结果:没有遇到异常。
然后我添加了以下代码来设置过滤
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 字段),它工作正常,但如果我们 t运行sfer 字段值 手动输入 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
,而是它的 Text
或 DisplayText
属性
这就是答案。因此,一个比你的更简洁的修复方法就是简单地做
procedure TForm1.DataSource1DataChange(Sender: TObject; Field: TField);
begin
sedNumber.Text := AdoTable1.FieldByName('ANumber').Text;
end;
并且在没有任何显式 Null 检查的情况下使异常静音。它之所以有效,是因为
procedure TFloatField.GetText
在 Data.DB 中显式 returns 空字符串作为 Text
字段包含空值时的值。
所以我追查到了问题所在。在我的 DataModule
的 OnDataChange
事件中,我调用了这个过程:
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;