以下是“真正的”Access (SQL) 解决方案的构建块。
观察#1
在我看来,好的第一步是将两个数字(长整数)列添加到 [SourceTable]:
[SubjectBlock] 将对主题相同的行的“块”进行编号
[SubjectBlockSeq] 将按顺序编号每个块内的行
他们都应该被索引(重复确定)。填充这些列的代码将是......
Public Sub UpdateBlocksAndSeqs()
Dim cdb As DAO.Database, rst As DAO.Recordset
Dim BlockNo As Long, SeqNo As Long, PrevSubject As String
Set cdb = CurrentDb
Set rst = cdb.OpenRecordset("SELECT * FROM [SourceTable] ORDER BY [DLID]", dbOpenDynaset)
PrevSubject = "(an impossible value)"
BlockNo = 0
SeqNo = 0
DBEngine.Workspaces(0).BeginTrans ''speeds up bulk updates
Do While Not rst.EOF
If rst!Subject <> PrevSubject Then
BlockNo = BlockNo + 1
SeqNo = 0
End If
SeqNo = SeqNo + 1
rst.Edit
rst!SubjectBlock = BlockNo
rst!SubjectBlockSeq = SeqNo
rst.Update
PrevSubject = rst!Subject
rst.MoveNext
Loop
DBEngine.Workspaces(0).CommitTrans
rst.Close
Set rst = Nothing
End Sub
...并且更新后的 SourceTable 将是...
DLID Subject Total SubjectBlock SubjectBlockSeq
1 Science 70 1 1
2 Science 60 1 2
3 Science 75 1 3
4 Science 70 1 4
5 Maths 80 2 1
6 Maths 90 2 2
7 English 90 3 1
8 English 80 3 2
9 English 70 3 3
10 Science 75 4 1
(请注意,我调整了您的测试数据,以便更容易验证下面的结果。)
现在,当我们遍历不断增加的“要包含在总数中的序列长度”时,我们可以通过使用类似...的查询来快速识别感兴趣的“块”。
SELECT SubjectBlock FROM SourceTable WHERE SubjectBlockSeq=3
...这将返回...
1
3
...表明在计算“运行 3”的总数时,我们根本不需要查看块 2(“数学”)和 4(最后一个“科学”)。
观察#2
第一次通过,当 NumRows=1 时,是一种特殊情况:它只是将 [SourceTable] 中的行复制到 [Expected Results] 表中。我们可以通过一个查询来节省时间:
INSERT INTO ExpectedResult ( DLID, NumRows, Subject, Total, SubjectBlock, NextSubjectBlockSeq )
SELECT SourceTable.DLID, 1 AS Expr1, SourceTable.Subject, SourceTable.Total,
SourceTable.SubjectBlock, SourceTable.SubjectBlockSeq+1 AS Expr2
FROM SourceTable;
您可能注意到我在 [ExpectedResult] 表中添加了两列:[SubjectBlock](和以前一样)和 [NextSubjetBlockSeq](只是 [SubjectBlockSeq]+1)。同样,它们都应该被索引,允许重复。我们将在下面使用它们。
观察#3
当我们继续寻找越来越长的“运行”来总结时,每次运行实际上只是一个较早(较短)的运行,并在末尾附加了一行。如果我们在进行过程中将结果写入 [ExpectedResults] 表,我们可以重复使用这些值,而无需费心返回并为整个运行添加各个值。
当 NumRows=2 时,“附加”行是SubjectBlockSeq>=2
...
SELECT SourceTable.*
FROM SourceTable
WHERE (((SourceTable.SubjectBlockSeq)>=2))
ORDER BY SourceTable.DLID;
...那是...
DLID Subject Total SubjectBlock SubjectBlockSeq
2 Science 60 1 2
3 Science 75 1 3
4 Science 70 1 4
6 Maths 90 2 2
8 English 80 3 2
9 English 70 3 3
...以及带有“较早(较短)运行”的 [ExpectedResult] 行,我们将在其上“附加”附加行
所以我们可以像这样获得新的总数并将它们附加到 [ExpectedResult]
INSERT INTO ExpectedResult ( DLID, NumRows, Subject, Total, SubjectBlock, NextSubjectBlockSeq )
SELECT SourceTable.DLID, 2 AS Expr1, SourceTable.Subject,
[ExpectedResult].[Total]+[SourceTable].[Total] AS NewTotal,
SourceTable.SubjectBlock, [SourceTable].[SubjectBlockSeq]+1 AS Expr2
FROM SourceTable INNER JOIN ExpectedResult
ON (SourceTable.SubjectBlockSeq = ExpectedResult.NextSubjectBlockSeq)
AND (SourceTable.SubjectBlock = ExpectedResult.SubjectBlock)
WHERE (((SourceTable.SubjectBlockSeq)>=2) AND (ExpectedResult.NumRows=1));
附加到 [ExpectedResult] 的行是
DLID NumRows Subject Total SubjectBlock NextSubjectBlockSeq
2 2 Science 130 1 3
3 2 Science 135 1 4
4 2 Science 145 1 5
6 2 Maths 170 2 3
8 2 English 170 3 3
9 2 English 150 3 4
现在我们在做饭...
使用与之前相同的逻辑,我们现在可以处理 NumRows=3。唯一的区别是我们会将值 3 插入 NumRows,我们的选择标准将是
WHERE (((SourceTable.SubjectBlockSeq)>=3) AND (ExpectedResult.NumRows=2))
完整的查询是
INSERT INTO ExpectedResult ( DLID, NumRows, Subject, Total, SubjectBlock, NextSubjectBlockSeq )
SELECT SourceTable.DLID, 3 AS Expr1, SourceTable.Subject,
[ExpectedResult].[Total]+[SourceTable].[Total] AS NewTotal,
SourceTable.SubjectBlock, [SourceTable].[SubjectBlockSeq]+1 AS Expr2
FROM SourceTable INNER JOIN ExpectedResult
ON (SourceTable.SubjectBlockSeq = ExpectedResult.NextSubjectBlockSeq)
AND (SourceTable.SubjectBlock = ExpectedResult.SubjectBlock)
WHERE (((SourceTable.SubjectBlockSeq)>=3) AND (ExpectedResult.NumRows=2));
并且附加到 [ExpectedResult] 的行是
DLID NumRows Subject Total SubjectBlock NextSubjectBlockSeq
3 3 Science 205 1 4
4 3 Science 205 1 5
9 3 English 240 3 4
参数化
由于每个连续的查询都非常相似,如果我们可以只编写一次并重复使用它会非常好。幸运的是,我们可以,如果我们把它变成一个“参数查询”:
PARAMETERS TargetNumRows Long;
INSERT INTO ExpectedResult ( DLID, NumRows, Subject, Total, SubjectBlock, NextSubjectBlockSeq )
SELECT SourceTable.DLID, [TargetNumRows] AS Expr1, SourceTable.Subject,
[ExpectedResult].[Total]+[SourceTable].[Total] AS NewTotal,
SourceTable.SubjectBlock, [SourceTable].[SubjectBlockSeq]+1 AS Expr2
FROM SourceTable INNER JOIN ExpectedResult
ON (SourceTable.SubjectBlock = ExpectedResult.SubjectBlock)
AND (SourceTable.SubjectBlockSeq = ExpectedResult.NextSubjectBlockSeq)
WHERE (((SourceTable.SubjectBlockSeq)>=[TargetNumRows])
AND ((ExpectedResult.NumRows)=[TargetNumRows]-1));
创建一个新的 Access 查询,将上面的内容粘贴到 SQL 窗格中,然后将其另存为pq_appendToExpectedResult
. (“pq_”只是一个视觉提示,它是一个参数查询。)
从 VBA 调用参数查询
您可以通过 QueryDef 对象在 VBA 中调用(执行)参数查询:
Dim cdb As DAO.Database, qdf As DAO.QueryDef
Set cdb = CurrentDb
Set qdf = cdb.QueryDefs("pq_appendToExpectedResult")
qdf!TargetNumRows = 4 '' parameter value
qdf.Execute
Set qdf = Nothing
何时停止
现在您可以看到这只是递增NumRows
和重新运行参数查询的问题,但是什么时候停止呢?这很容易:
在 VBA 中增加 NumRows 变量后,测试
DCount("DLID", "SourceTable", "SubjectBlockSeq=" & NumRows)
如果它返回 0 那么你就完成了。
给我看(全部)代码
对不起,不是马上。;) 玩这个,让我们知道它是怎么回事。