レポートでグループ毎にページ番号とグループ内のページ数を表示する方法

レポートの機能を使って、グループ毎にページ番号とページ数を印刷することができます。レポートでは[Pages]を使って全体のページ数を各ページに印刷することができますが、この時Accessは全ページを2回スキャンしています。これを利用して1回目のスキャンで各グループのページ数を配列に保存しておき、2回目のスキャンで配列からページ数を取り出してコントロールに値をセットするのです。

具体的には、レポートに以下のプロシージャを記述してください。そうすれば[ページ数表示]というコントロールに表示されます。注意点として[Pages]をダミーで配置させておくことです。[ページ数表示]は、ページヘッダ・ページフッタどちらでもOKです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Option Compare Database
Option Explicit
 
Dim gps(1000) As Integer 'グループ毎のページ数を保存する配列
Dim i As Integer
 
Private Sub レポートヘッダー_Format(Cancel As Integer, FormatCount As Integer)
i = 0
End Sub
 
Private Sub グループフッター0_Format(Cancel As Integer, FormatCount As Integer)
If FormatCount = 1 Then
If Pages = 0 Then gps(i) = Page
i = i + 1
Page = 0
End If
End Sub
 
Private Sub ページヘッダー_Format(Cancel As Integer, FormatCount As Integer)
If Pages > 0 Then ページ表示 = Page & "/" & gps(i)
End Sub

レポートで複数レコードを横一行に表示させる方法

通常、詳細セクションは次のレコードに移るときに行を換えてしまいますが、MoveLayout プロパティを False に設定することで同じ行に重ね打ちします。さらに、コントロールの Left プロパティを設定すると印字する左右の位置を設定できますので、この2つを使うと複数レコードを横一行に印字できます。

下の例は、dataというフィールドがあるテーブルの複数レコードを一行に印字する例です。実際には、右いっぱいになったら改行するとか、別のフィールドでグループ化して改行するとかを考慮する必要がありますがこれは簡単にできるでしょう。

1
2
3
4
5
6
7
8
9
10
11
12
Option Compare Database
Option Explicit
Dim i As Integer
 
Private Sub 詳細_format(Cancel As Integer, PrintCount As Integer)
data.Left = (i + 2) * 567
MoveLayout = False
End Sub
 
Private Sub 詳細_Print(Cancel As Integer, PrintCount As Integer)
i = i + 1
End Sub

切り捨て・切り上げ・四捨五入

下の例は0に関して対称に丸めます。切り上げに関しては0.9を足して切り捨てをする方法がNifty Forumに紹介されているのを見かけますが間違いですので気をつけてください。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
'切り捨て
Function Floor(x As Double) As Long
Floor = Fix(x)
End Function

'切り上げ
Function Ceil(x As Double) As Long
Ceil = -Sgn(x) * Int(-Sgn(x) * x)
End Function

'四捨五入
Function Round(x As Double) As Long
Round = Fix(x + 0.5 * Sgn(x))
End Function

リンクテーブルで Seek を使う方法

レコードセットで検索をする場合、FindFirstよりもIndexを使ったSeekを使ったほうが速いとされています。Seekを使うためには、dbOpenTableを指定してレコードセットを開く必要があります。次の手順を参考にしてください。

1
2
3
4
Set db = DBEngine(0).OpenDatabase("テーブルを保存しているファイル")
Set rec = db.OpenRecordset("テーブル",dbOpenTable)
rec.Index = "PrimaryKey"
---- 以後はオンラインヘルプを参照 ----

トランザクションと定義域集合関数

トランザクション中に定義域集合関数を使う場合には注意が必要です。トランザクションを開始してから加えた変更は DLookup などの定義域集合関数には反映されません。それに対し、Recordsetオブジェクトを使った場合はコミットする前でも変更が反映されます。

以下のサンプルはトランザクション中で、レコードのないテーブルに一件のレコードを追加し、これをDLookupとRecordsetの2つの方法で表示させようというものです。フィールド:f1を持つテーブル:T1を予め作成してこのプロシージャを実行してください。

1
2
3
4
5
6
7
8
9
10
11
Sub Temp()
BeginTrans
CurrentDb.Execute "INSERT INTO T1 (F1) VALUES(100)", dbFailOnError
MsgBox "F1(DLookup):" & DLookup("F1", "T1")
With CurrentDb.OpenRecordset("T1", dbOpenDynaset)
.MoveFirst
MsgBox "F1(Recordset):" & !f1
.Close
End With
CommitTrans
End Sub

トランザクションとRunSQLメソッド

RunSQLメソッドではトランザクションが効きません。トランザクション中ではExecuteメソッドを使う必要があります。

以下のサンプルはトランザクション中に、レコードのないテーブルに一件のレコードを追加し、ロールバックでもとに戻すものですが、RunSQLメソッドを使った場合は元に戻らず、トランザクションが効いていないことがわかります。フィールド:f1を持つテーブル:T1を予め作成してこのプロシージャを実行してください。RunSQLの行をコメントにして、Executeメソッドの行のコメントを外して実行するとロールバックで元に戻ります。

1
2
3
4
5
Sub Temp() 
BeginTrans DoCmd.RunSQL "INSERT INTO T1 (F1) VALUES(100)"
' CurrentDb.Execute "INSERT INTO T1 (F1) VALUES(100)", dbFailOnError
Rollback MsgBox "F1(DLookup):" & DLookup("F1", "T1")
End Sub