【问题标题】:access report from dynamic crosstab query and vba to "manually" generate reports从动态交叉表查询和 vba 访问报告以“手动”生成报告
【发布时间】:2023-03-11 03:21:01
【问题描述】:

我遇到了生成复杂访问报告的问题(复杂是指数据处理、可变数量的字段等)。
让我更详细地解释一些我需要实现的东西:

  • 某些字段不应根据查询中的某些值显示
  • 如果某条记录不存在,则应显示一条漂亮的彩色(非常明显)消息,而不是那里的值(例如,假设存在日期字段中包含 03/04/2009 的记录,日期字段中 03/06/2009 的记录也存在,但不存在 03/05/2009 的记录。在显示与最后一条记录相关的数据之前,我应该打印类似“03/05 没有出现” /2009")
  • 一个条形图,它的数据不是记录中的值,而是根据一组记录计算的其他数据(例如某个日期的所有成绩的平均值)。此图表中的系列数也根据记录中的值而有所不同,此图表不会位于详细信息部分,而是位于页面标题或某种组标题中。

还需要说明的是,该查询是一个TRANSFORM查询(更准确地说,是许多TRANSFORM查询的INNER JOIN),因此查询返回的列数会有所不同。虽然在过去我一直无法将此查询绑定为报告的记录源,但不知何故 Access 现在停止了抱怨(有人可以澄清一下吗?这是否正常,我应该不用担心并将其用作记录源或我应该避免吗?)

有两种选择可以实现我想要的(我现在可以看到):

  1. 创建一个没有记录源和大量未绑定字段的报表,并通过多个事件(Report_Open、Section_Format 等)并在 DAO 的帮助下手动设置这些字段的值。也可以通过 VBA 更改图表的数据系列。
  2. 将记录源设置为查询,并创建一些疯狂而令人困惑的 VBA 代码来处理数据并实现我需要的一切。

在我看来,选项 2 将是一个巨大的头痛和浪费时间,我认识到选项 1 非常类似于写入 Excel 文件(因为所有数据都是通过 DAO 获得的),这将是容易得多,因为我可以更好地控制那里的几乎所有内容(但出于许多其他原因,我们希望所有内容都包含在访问报告中)

虽然我有偏见并打算选择选项 1,但我发现此选项存在一些问题,例如:

  1. 我找不到使用 VBA 在报表中创建新页面的方法,因此我仅限于第一页。
  2. 缺乏关于 VBA 和访问报告的某种免费、在线、体面和完整的文档

另外,如果选项 2 更可行,我当然愿意接受,但我还需要一些建议,也许还有一些技巧来解决我在这个问题中提到的问题。

所以,问题是:

  • 在哪里可以找到有关 Access Reports 和 VBA 的一些体面和完整的文档?
  • 如何在访问报告中创建页面,并选择要写入的页面?
  • 对于我手头的问题,我是否会遇到任何我应该知道的瓶颈?我是否应该已经在考虑 Access Reports 的替代方案(例如写入电子表格?)

【问题讨论】:

  • 您是否考虑过在 Word(自动化)或 HTML 中创建报告?
  • 我想尽可能坚持访问报告,仅在必要时切换

标签: sql ms-access vba report


【解决方案1】:

听起来您想dynamically create the report 并避免使用所有虚拟文本框。

【讨论】:

  • 这是一篇非常棒的文章,但对我来说,为重复活动创建即时报告似乎过于复杂,唯一改变的是交叉表取决于特定的数据集。
  • 您可能会发现至少有 3-5 列,因此可以动态创建其余列。定位在交叉表中会很复杂。在设计模式下以编程方式打开报表怎么样?
  • 对我来说,必须在设计模式下打开任何对象都是错误的。一方面,它不适用于 MDE/ACCDE。此外,如果您尝试动态添加控件,您最终会用完,因为在表单/报告的生命周期内有 700 多个控件的限制,无论您是删除一些还是不保存物体。所以,一般来说,根本不建议这样做。
  • 我认为这会带来问题。似乎有点像黑客。我继承了一个执行此操作的应用程序,并且似乎保存更改有点不稳定。
【解决方案2】:

关于:

我找不到创建新页面的方法 在 VBA 的报告中,因此我 仅限于第一页。

您的解决方案 #1 似乎假设一个未绑定的报告。

我认为我要做的是将交叉表作为行源的形式,因此您将有记录来生成页面,然后在没有 ControlSource 的情况下定义报表的控件(绑定到字段的控件除外始终存在于 CrossTab 中)。然后,您可以在运行时根据特定列分配 ControlSource。这是从我正在开发的应用程序中获取的交叉表的 SQL:

  TRANSFORM First(impNoMatch.PersonID) AS FirstOfPersonID
  SELECT impNoMatch.LastName, impNoMatch.FirstBame
  FROM impNoMatch
  GROUP BY impNoMatch.LastName, impNoMatch.FirstName
  PIVOT impNoMatch.Status;

现在,您知道 SELECT 子句中的字段将始终存在,因此如果您在正在使用的 SQL 字符串上打开记录集并计算记录集的 Fields 集合中的字段数(您不能使用报告的记录集,除非它是 ADO 记录集,即未绑定到记录源):

  Dim strSQL As String
  Dim rsFields As DAO.Recordset
  Dim lngFieldCount As Long

  strSQL = Me.Recordsource
  Set rsFields = CurrentDB.OpenRecordset(strSQL)
  lngFieldCount = rsFields.Fields.Count

由此,由于您知道 SELECT 语句中的字段数(即行标题),您可以计算要分配的动态控件的数量,并且可以使用此记录集的字段集合来分配 ControlSources并取消隐藏控件。

您将从所有将显示动态字段集的控件开始,因此它们的 Visible 属性为 FALSE。您还可以为这些控件使用命名约定。在下面的代码中,我使用了 txtNN,其中 NN 是 Fields 集合中的数字索引,格式为 2 位。这是代码(它在上面列出的内容中添加了行,并在 OnOpen 事件中运行):

  Dim strSQL As String
  Dim rsFields As DAO.Recordset
  Dim lngFieldCount As Long
  Dim l As Long
  Dim strControlName As String

  strSQL = Me.RecordSource
  Set rsFields = CurrentDb.OpenRecordset(strSQL)
  lngFieldCount = rsFields.Fields.Count
  For l = 2 To lngFieldCount - 1
    strControlName = "txt" & Format(l, "00")
    Me(strControlName).ControlSource = rsFields.Fields(l).Name
    Me(strControlName).Visible = True
  Next l
  rsFields.Close
  Set rsFields = Nothing

现在,如果您想变得花哨,您可以重新格式化控件,更改宽度和水平/垂直位置。如果你这样做,你必须在不同的事件中这样做,而且选择它有点棘手。放置它的唯一好地方是报表组标题的 OnFormat 事件。如果您没有任何分组,您可以添加一个不执行任何操作的分组。对于我的交叉表,Lastname 和 Firstname 上的两级排序以及 Firstname 组上没有任何内容的标题是使用 OnFormat 事件更改报表控件外观/布局的好地方。

关于如何学习如何做到这一点的问题,我建议您选择一本中级/高级 Access 编程书籍。 Access 开发人员手册是这方面的黄金标准,其中包含大量报告程序控制的示例。

【讨论】:

  • 感谢您的回答。虽然我以前更喜欢选项#1,但无法手动创建页面使我接近了#2。由于报表中要显示的数据并不完全是记录中的数据,所以我一直在使用标签,并通过 VBA 和 DAO 手动设置它们的标题。此外,我一直在使用大量 Me.NextRecord=False 和 Me.NextRecord=True 来填充不在 Reports RecordSource 中的 DAO RecordSet 的值(我必须承认,到目前为止,这里有很多工作)。但是,我认为我可能会成功 :) 另外,非常感谢您提供的文档参考。
  • 在我看来,您最好在 HTML 中或通过自动化 Word 执行此操作。访问报表不能很好地处理任意输出——它们是建立在报表表面将与其背后的记录紧密绑定的假设之上的。
猜你喜欢
  • 1970-01-01
  • 2014-09-17
  • 2018-04-11
  • 1970-01-01
  • 1970-01-01
  • 2014-02-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多