「実際の」アクセス(SQL)ソリューションの構成要素は次のとおりです。
観察#1
最初のステップとして、[SourceTable]に2つの数値(長整数)列を追加することをお勧めします。
[SubjectBlock]は、件名が同じである行の「ブロック」に番号を付けます
[SubjectBlockSeq]は、各ブロック内の行に順番に番号を付けます
両方ともインデックスを付ける必要があります(複製OK)。これらの列に入力するコードは次のようになります...
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(最後の「科学」の1つ)をまったく調べる必要がないことを示します。
観察#2
NumRows = 1の場合、最初の通過は特殊なケースです。[SourceTable]から[ExpectedResults]テーブルに行をコピーするだけです。単一のクエリでそれを行うことで時間を節約できます。
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)の2つの列を追加したことに気付くかもしれません。繰り返しになりますが、重複を許可するために、両方にインデックスを付ける必要があります。以下で使用します。
観察#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に戻ったら、完了です。
(すべての)コードを見せて
申し訳ありませんが、すぐにではありません。;)これをいじって、どうなるか教えてください。