从动态交叉表查询中访问报告并使用VBA“手动”生成报告

3
我遇到了生成复杂访问报表的问题(所谓复杂是指数据处理、字段数量可变等)。
让我更详细地解释一些我需要实现的东西:
- 某些字段根据查询中的某些值不应显示。 - 如果某个记录不存在,则应该出现一个漂亮的彩色(非常引人注目的)消息,而不是那里本来应该有的值(例如,假设日期字段中存在日期为03/04/2009和03/06/2009的记录,但没有日期为03/05/2009的记录。在显示与最后一个记录相关的数据之前,我应该打印类似于“未在03/05/2009上出现”的内容)。 - 条形图将作为数据而不是记录中的值,以计算一组记录中的其他内容(例如某个日期的所有成绩的平均值)。此图表的系列数也会根据记录中的值而变化,此图表不会出现在详细信息部分,而是出现在页面标题或某种组标题中。
还应该提到,查询是一个转换查询(更准确地说,是许多转换查询的内部联接),因此由查询返回的列数是可变的。虽然过去我无法将此查询绑定为报表的记录源,但现在 Access 停止抱怨了(请问有人可以澄清这一点吗?这是正常的吗?我不用担心它并将其用作记录源,还是应该避免使用它?)
实现我想要的有两个选项(目前我能看到的):
- 创建一个没有记录源但有很多未绑定字段的报表,并通过几个事件(Report_Open、Section_Format 等)和 DAO 的帮助手动设置这些字段的值。通过 VBA 也可以更改图表的数据系列。 - 将记录源设置为查询,并创建一些疯狂而令人困惑的 VBA 代码来处理数据并实现我需要的所有内容。
对我来说,选项 2 看起来会非常头痛和浪费时间,而我认为选项 1 几乎就像写入 Excel 文件一样(因为所有数据都是通过 DAO 获取的),这将更容易,因为我在那里几乎可以控制所有东西(但出于许多其他原因,我们希望所有东西都在访问报表中)。
虽然我有偏见并打算选择选项 1,但我发现这个选项存在几个问题,例如:
- 我找不到用 VBA 创建新页面的方法,因此我只能限制在第一页。 - 缺乏某种免费、在线、体面且完整的 VBA 和 Access 报表文档。
此外,如果方案二更可行,我当然愿意采取该方案,但我也需要一些建议,并且可能需要一些解决我在此问题中提到的问题的提示。
所以,问题是:
1.在哪里可以找到有关Access报告和VBA的合适和完整的文档?
2.如何在Access报告中创建页面,并选择要写入哪个页面?
3. 针对我手头的问题,是否会遇到任何瓶颈? 我是否应该考虑使用其他替代方案来解决 Access Reports 的问题(例如写入电子表格)?

你是否考虑过创建一个Word报告(自动化)或使用HTML呈现报告? - Fionnuala
我希望尽可能地坚持使用Access报告,只有在必要时才切换。 - Marcelo Zabani
2个回答

1

这是一篇非常棒的文章,但对我来说似乎过于复杂了,因为针对重复活动创建即时报告,唯一变化的是交叉表中确切列数取决于特定数据集。 - David-W-Fenton
您可能会发现至少有3-5列,因此其余部分可以动态创建。定位将像交叉表中一样复杂。那么在设计模式下通过编程方式打开报告怎么样? - JeffO
对我来说,必须在设计模式下打开任何对象都是一个错误。首先,在MDE/ACCDE中无法正常工作。此外,如果您试图动态添加控件,最终会用完限制为700个左右的控件数量,无论您是否删除了一些或未保存该对象。因此,总的来说,这不可取。 - David-W-Fenton
我认为这可能会带来问题。看起来有点像hack。我继承了一个应用程序,它是这样做的,似乎更改的保存有点不稳定。 - JeffO

0
关于:

我找不到用VBA创建新报表页面的方法,因此我只能限制在第一页。

您的解决方案#1似乎假定未绑定的报表。

我认为我会将交叉表作为行源放在表单中,这样您就有记录来生成页面,然后定义报表控件时不使用ControlSource(除了绑定到交叉表中始终存在的字段的控件)。然后,您可以根据特定列在运行时分配ControlSources。以下是我现在从应用程序中获取的交叉表的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字符串上打开记录集并计算记录集的字段集合中的字段数(除非它绑定到Recordsource,否则无法使用报表的Recordset,即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是格式化为2位数字的Fields集合中的数值索引。以下是代码(添加到上面列出的内容中,并在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 Developers Handbook》是这方面的黄金标准,其中包含大量程序控制报表的示例。


谢谢您的答复。虽然我过去更喜欢选项#1,但无法手动创建页面使我采取了方法#2。由于报告中要显示的数据不是记录中精确的数据,因此我一直在使用标签,并通过VBA和DAO手动设置它们的标题。另外,我一直在使用大量的Me.NextRecord = False和Me.NextRecord = True来填充来自DAO RecordSets而不在报表RecordSource中的值(到目前为止做了很多劳动,我必须承认)。然而,我认为我可能会做到:) 此外,非常感谢文档参考。 - Marcelo Zabani
听起来你最好使用HTML或自动化Word来完成这个任务。Access报表不太适合任意输出,因为它们是基于报表表面与其背后的记录紧密绑定的假设构建的。 - David-W-Fenton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接