MS Access VBA - 在数据表子窗体中显示动态构建的 SQL 结果

Posted

技术标签:

【中文标题】MS Access VBA - 在数据表子窗体中显示动态构建的 SQL 结果【英文标题】:MS Access VBA - display dynamically built SQL results in datasheet subform 【发布时间】:2014-01-21 23:52:08 【问题描述】:

我在 MS Office 应用程序(用于自动化和 ETL 流程)中使用 VBA 有几年的经验,但直到最近才需要在 MS Access 中处理表单。我正在为我设计的数据库设计一些简单的数据提取表单,但我正忙于一项看似简单的任务。

目标:我需要一个数据表子窗体来显示从主窗体上的控件动态构建的 SQL 语句返回的记录。

在我的主窗体上,我有一个按钮,当用户单击它时,该按钮会将用户在其他用户窗体控件中指定的信息编译为 SQL 查询,然后运行该查询,以便子窗体显示结果记录。

无论我做什么,我都无法让它工作。我不断收到(无论如何大部分时间)Microsoft Visual Basic 运行时错误“'2467':您输入的表达式指的是已关闭或不存在的对象。”这就是我使用下面显示的代码时遇到的错误。我不知道我是否需要在任何代码运行后立即启动子表单或什么。我已经尝试了其他代码论坛中也无法使用的其他代码变体,但我似乎发现了几个论坛主题,包括 Stack Overflow 上的一些主题,这些主题表明我下面的代码应该可以工作。

所附图片显示了基本主窗体的外观。我已经标记了用户将单击的按钮 (btnDisplaySWData) 以编译从尚未包含的控件创建的 SQL,但这不是问题。我只是硬编码一个 SQL 语句,如代码 sn-p 所示,试图找出这个问题。如前所述,我希望记录显示在名为 dataDisplaySubform 的子表单中。 “JUNK”是 Access 数据库中的一个表,我可以使用下面的 SQL 代码合法地查询它,我只是将其用于测试目的,直到我弄清楚这一点。显示的数据表单(名为 frmDataExtract)中的所有代码都包含下面代码窗口中的内容。

Option Compare Database
Option Explicit
Public Sub btnDisplaySWData_Click()
    Dim pSQL As String
    pSQL = "SELECT JUNK.agency_ID, JUNK.agency_desc FROM JUNK"
    Me.dataDisplaySubform.Form.RecordSource = pSQL
End Sub

该表单名为 dataDisplaySubform,如下面选中子表单的属性截图所示。

这就是整个表单布局的样子

我已经搜索了几个论坛站点,并且还尝试了搜索 Stack Overflow 的各种术语以找到解决我的问题的潜在解决方案,但即使原始线程被发布者标记为已解决,也没有任何工作。我花了太多时间,大约 2 个工作日,试图找出我做错了什么,但还没有弄清楚。

感谢任何可以帮助我朝着正确方向前进的人,这让我发疯了。

谢谢, --TB

TURKISHGOLD 编辑解决方案

好吧,我想我是自己想出来的,尽管 HansUp 帮助我走上了这条路,提到子表单源对象没有分配任何东西。就我而言,将源对象分配给表单并不是 HansUp 所建议的正确解决方案。相反,保存的查询似乎可以让它做我想做的事。

不确定是否有更好的方法来执行此操作,但您似乎需要设置一个虚拟的、几乎是占位符的查询,因此您可以在 VBA 中将子表单 Source Object 设置为它。 像这样的占位符查询:

SELECT * FROM JUNK WHERE JUNK.agency_ID ="_";

以上 Access 查询保存为名称“TESTQUERY”。它不显示任何内容,但满足将源对象分配给某些内容的需要,本质上是在表单视图中查看主表单时实例化子表单。因此,使用占位符保存的查询,您可以将 RecordSource 重新分配给通过主窗体上的用户界面控件组合在一起的任何 SQL 字符串,如下所示:

Public Sub btnDisplaySWData_Click()
    Dim pSQL As String
    pSQL = "SELECT JUNK.agency_ID, JUNK.agency_desc FROM JUNK"
    Me.dataDisplaySubform.SourceObject = "Query.TESTQUERY"
    Me.dataDisplaySubform.Form.RecordSource = pSQL
    Me.dataDisplaySubform.Requery
End Sub

当表单投入生产时,存储在 pSQL 字符串变量中的硬编码 SQL 语句将通过用户在主表单控件上的输入组合在一起。

所以现在,当单击 btnDisplaySWData 时,它会执行我尝试执行的操作并显示记录。

【问题讨论】:

子表单的名称与要显示的表单无关。屏幕截图清楚地显示没有为子表单控件设置表单。子表单控件可以放置在表单上,​​但在您将源对象属性设置为合法(现有)表单之前,它不会显示任何表单。如果没有要显示的表单,则无法设置数据源。因此,在您从源对象属性中看到的下拉列表中选择一个表单之前,您拥有正确的子表单控件名称这一事实是没有意义的。此处无需使用或设置虚拟查询。您必须设置源对象属性 = 合法形式。 【参考方案1】:

短而甜美。这是创建动态 sql 字符串的按钮的代码,关闭当前对象(以防它打开),删除临时查询定义(因为我们需要一个),使用新 sql 创建新查询定义,更改记录源Bob 是你的叔叔。

Private Sub btnRunSQL_Click()
  'my subform is called datasheet, i know -- dumb name.
  'a dynamic sql needs to be saved in a temporoary query. I called my qtemp
  Dim sql As String
  sql = "select * from client order by casename asc"
  'in case there is something kicking around, remove it first, otherwise we can't delete the temp query if it is still open
  Me!Datasheet.SourceObject = ""
  'delete our temporary query. Note, add some err checking in case it doesn't exist, you can do that on your own.
   DoCmd.DeleteObject acQuery, "qtemp"
  'lets create a new temporary query
  Dim qdf As QueryDef

  Set qdf = CurrentDb.CreateQueryDef("qtemp", sql)
  'set the subform source object
  Me!Datasheet.SourceObject = "query.qtemp"
  'and it should work.
End Sub

【讨论】:

我认为这是最好的答案。【参考方案2】:

如果在Me.dataDisplaySubform.Form.RecordSource 行出现“对象已关闭或不存在” 错误,则可能是您的子表单控件 未命名 dataDisplaySubform.

您可以通过对代码的临时更改来检查所有表单子表单控件的名称...

'Me.dataDisplaySubform.Form.RecordSource = pSQL
Dim ctl As Control
For Each ctl In Me.Controls
    If TypeName(ctl) = "SubForm" Then
        Debug.Print ctl.Name, TypeName(ctl)
    End If
Next
Stop

Stop 语句将触发调试(中断)模式并带您进入立即窗口,您可以在其中查看表单的子表单控件的名称。

您添加到问题中的屏幕截图确认您为子表单控件使用了正确的名称。但是,该子表单的 Source Object 属性中没有任何内容。由于那里没有表单,因此错误消息的第二部分 “不存在” 适用。 Me.dataDisplaySubform.Form没有可以引用的表单

【讨论】:

我在上面编辑了我的问题,以在属性中显示带有子表单的名称,这表明它被命名为 dataDisplaySubform。我还运行了您的代码并得到了相同的结果:在即时窗口中,控件的名称是“dataDisplaySubform”。 将表单的名称放在Source Object 属性中。 我可以在 Source Object 属性中输入的唯一表单名称是父表单的名称,即 frmDataExtract,它是 dataDisplaySubform 所在的表单。当我这样做并转到表单视图时,我收到一条警告,指出“您不能在其内部放置表单(或报表)。选择或输入不同的表单或报表以用作子表单或子报表”。如果我然后单击确定,然后单击按钮运行代码,我会得到相同的 2467 错误,突出显示 Me.dataDisplaySuform.Form.RecordSource = pSQL 行。您是否在 Source Object 属性中指定了某些内容? 您是否打算更改当前表单的RecordSource?如果是这样,Me.RecordSource = pSQL 我不确定你在追求什么,但至少我们已经诊断出错误消息。 :-) 抱歉回复延迟,今天工作中的互联网连接已被顶起。我上面提到的目标是让名为 dataDisplaySubform 的数据表子表单显示从动态构建的 SQL 语句返回的记录,该语句是通过父 frmDataExtract 上的控件(尚未放置)中的用户输入创建的。您提到“更改当前表单的 RecordSource”-您是指放置子表单的父 frmDataExtract 吗?如果是,我如何修改父 frmDataExtract 并将更改传播到子表单?我可能不明白【参考方案3】:

对其他读者的一些澄清:

详细视图子表单的 sourceObject 属性确定显示哪些列/字段。因此,您可以将其设置为表或查询,然后可以选择使用过滤器不返回任何记录(如果您希望记录集最初为空白),或者作为使用自定义 SQL 的 recordSource 的替代方法。

recordSource 可以是任何表、查询或 SQL,但子表单将仅显示名称与 sourceObject 的字段匹配的字段。例如,如果您将 sourceObject 设置为表,然后将 recordSource 设置为具有部分重叠字段名称的查询(Access 将显示所有列,但只有重叠的列中会有数据),这可能会造成混淆。

要拥有一个显示任意 SELECT 语句或允许用户选择要在哪个表上进行 SELECT 的表单,可以将他们的输入保存为新查询(或覆盖现有的命名查询),然后设置sourceObject (必须关闭表单然后重新打开才能显示新列,因此您可能需要打开一个弹出窗口或新选项卡来显示结果)。

【讨论】:

【参考方案4】:

使用CreateQueryDef 然后Me.dataDisplaySubform.SourceObject = "Query.NewqueryName"NewQueryName 是使用createQueryDef 创建时给出的名称

【讨论】:

以上是关于MS Access VBA - 在数据表子窗体中显示动态构建的 SQL 结果的主要内容,如果未能解决你的问题,请参考以下文章

如何遍历所有子窗体 MS Access VBA

专家 - 表单打开方式不同于设计视图与 MS Access 对象列表

在子窗体中输入数据时,MS Access 会自动在主窗体中填写 id

Access VBA:在连续子窗体中编辑 RecordSet

Ms Access中更新主窗体时更新子窗体的相关字段

在查询子窗体 Access VBA 中更新之前编辑记录