Private Sub Form_Unload(Cancel As Integer)
If Me.埋め込み1.Form!SumF2.Value <> Me.埋め込み1.Form!SumF4.Value Then
Cancel = True
Me.埋め込み1.Form!SumF2.SetFocus
End If
End Sub
Private Sub 埋め込み1_Exit(Cancel As Integer)
Me.埋め込み1.Form.Requery 'レコード保存
If Me.埋め込み1.Form!SumF2.Value <> Me.埋め込み1.Form!SumF4.Value Then
Cancel = True
MsgBox "F2とF4の合計が合っていません!"
End If
End Sub
Private Sub Report_Unload(Cancel As Integer)
Dim res As String
res = MsgBox("正常に印刷が完了しましたか?", vbQuestion + vbYesNo, "完了確認")
If res = vbNo Then
Cancel = True
Else
MsgBox "さようなら"
End If
End Sub
'ファイルパスとテーブル名は適宜書き換えること
Sub Test1()
Dim strDatabasePath As String
strDatabasePath = "C:\FolderName\FileName.accdb"
If Dir(strDatabasePath) = "" Then
MsgBox "Access データベースファイル'" & strDatabasePath & "'が見つかりません。", _
vbExclamation, _
"ファイル参照エラー"
Exit Sub
End If
Dim adoCn As ADODB.Connection
Dim strConnectString As String
Set adoCn = New ADODB.Connection
With adoCn
.CursorLocation = adUseClient
.CommandTimeout = 60
strConnectString = "Provider=Microsoft.ACE.OLEDB.12.0;Mode=Read;" & _
"Data Source=" & strDatabasePath
Debug.Print strConnectString
.ConnectionString = strConnectString
.Open
End With
Dim adoRs As ADODB.Recordset
Dim strSQL As String
Set adoRs = New ADODB.Recordset
With adoRs
Set .ActiveConnection = adoCn
.CursorLocation = adUseClient
.CursorType = adOpenStatic
.LockType = adLockReadOnly
strSQL = "SELECT t1.[sptx], t1.[sbj], t1.[pymt], t1.[fwd], t1.[dat]" & _
" FROM [テーブル名] t1" & _
" ORDER BY t1.[id];"
Debug.Print strSQL
.Source = strSQL
.Open
End With
Dim adoFld As ADODB.Field
Dim wbkDestination As Excel.Workbook
Dim wsDestination As Excel.Worksheet
Dim lngColumn As Long
Set wbkDestination = Workbooks.Add
Set wsDestination = wbkDestination.Worksheets(1)
wsDestination.Cells(1, 1).Resize(1, adoRs.Fields.Count).NumberFormat = "@"
For lngColumn = 1 To adoRs.Fields.Count
Set adoFld = adoRs.Fields(lngColumn - 1)
wsDestination.Cells(1, lngColumn).Value = adoFld.Name
Set adoFld = Nothing
Next
wsDestination.Cells(2, 1).CopyFromRecordset adoRs
wsDestination.UsedRange.EntireColumn.AutoFit
adoRs.Close
Set adoRs = Nothing
adoCn.Close
Set adoCn = Nothing
Set wsDestination = Nothing
Set wbkDestination = Nothing
End Sub
Dim dic As Object
Set dic = CreateObject("Scripting.Dictionary")
Dim fld As ADODB.Field, i As Long
For Each fld In adoRs.Fields
dic(fld.Name) = i
i = i + 1
Next fld
Debug.Print myArray(dic("フィールド名"), x)
Dim 検索フォルダ As Folder
Set 検索フォルダ = FSO.GetFolder("C:\Users\○○\Desktop\検索フォルダ")
Dim 該当フォルダ As Folder
Dim フォルダ候補 As Folder
For Each フォルダ候補 In 検索フォルダ.SubFolders
If フォルダ候補.Name Like "売上データ*" Then
Set 該当フォルダ = フォルダ候補
Exit For ElseIf
'フォルダ候補(子フォルダ)を親として同じことをする
End If
Next
If Not 該当フォルダ Is Nothing Then
MsgBox "該当フォルダのパス:" & 該当フォルダ.Path
End If
End Sub
とするだけですね
Sub フォルダ名が売上データ○○になっているフォルダを取得する()
Dim FSO As New FileSystemObject
Dim 検索フォルダ As Folder
Set 検索フォルダ = FSO.GetFolder("C:\Users\○○\Desktop\検索フォルダ")
Dim 該当フォルダ As Folder
Dim フォルダ候補 As Folder
For Each フォルダ候補 In 検索フォルダ.SubFolders
If フォルダ候補.Name Like "売上データ*" Then
Set 該当フォルダ = フォルダ候補
Exit For
ElseIf
Dim 孫フォルダ As Folder
For Each 孫フォルダ In フォルダ候補.SubFolders
If フォルダ候補.Name Like "売上データ*" Then
Set 該当フォルダ = 孫フォルダ
Exit For
End If
Next
If Not 該当フォルダ Is Nothing Then
Exit For
End If
End If
Next
If Not 該当フォルダ Is Nothing Then
MsgBox "該当フォルダのパス:" & 該当フォルダ.Path
End If
End Sub
貴重なご意見をありがとうございました。
壊れた時のリカバリー・オブジェクト数との兼ね合いを見て感が陽と思います。
一般論ではなく前回の回答の”フォーカス喪失時”と”フォームの読み込み解除時”についてということで回答します。
その前にメイン/サブフォーム形式のフォームのフォーカス関係のイベントについて。
まずフォームを開いた直後は、メインフォームのタブオーダーが1番目のコントロールにフォーカスがあります。
メインフォームの入力を終えてサブフォームの入力に移るためにサブフォームのコントロールをクリックしたとします。
フォーカス関係イベントは下記のように発生します。
メインフォーム上のサブフォームコントロールのフォーカス取得時(Enter)イベント
↓
サブフォーム上のコントロールのフォーカス取得時(Enter)イベント
↓
サブフォーム上のコントロールのフォーカス取得後(GotFocus)イベント
サブフォームの入力を終了してメインフォームのコマンドボタンやテキストボックスをクリックしてメインフォームへフォーカスを移動したとき
サブフォーム上のコントロールのフォーカス喪失時(Exit)イベント
↓
サブフォーム上のコントロールのフォーカス喪失後(LostFocus)イベント
↓
メインフォーム上のサブフォームコントロールのフォーカス喪失時(Exit)イベント※
↓
クリックしたメインフォーム上のコントロールのフォーカス取得時(Enter)イベント
前回の回答では上記の※の時に、2つの合計をチェックして不一致の場合は、Cancel = Trueでイベントをキャンセルします。イベントがキャンセルされるとそれ以降のイベントは発生せずフォーカスはサブフォームから移動しません。
フォームの読み込み解除時イベントはフォームの右上のクローズボタンをクリックするなどフォームを閉じようとするアクションを起こすと発生します。サブフォームにフォーカスがある場合は、その前にメインフォームへフォーカスが移動することになります。イベントの発生順は下記のようになります。
サブフォーム上のコントロールのフォーカス喪失時(Exit)イベント
↓
サブフォーム上のコントロールのフォーカス喪失後(LostFocus)イベント
↓
メインフォーム上のサブフォームコントロールのフォーカス喪失時(Exit)イベント※
↓
メインフォームの読み込み解除時(Unload)イベント
この場合も※のところで合計チェックをして不一致ならイベントをキャンセルしますが、読み込み解除時イベントは発生してしまいます。ということは合計が不一致のまま閉じてレコード保存されてしまうことになります。
それを防ぐために、読み込み解除時でも合計をチェックして不一致ならイベントをキャンセル(Cancel = True)してフォーカスをサブフォームへ戻しておきます。これで合計が不一致の場合はフォームを閉じることができなくなります。
すみません、合計値が合致していない時は"F2とF4の合計が合っていません!"のメッセージが出て先に進めなくて狙い通りなのですが、合致している(正常の合計値)場合にフォーム閉じると ”メソッドまたはデータ メンバが見つかりません。” のコンパイルエラーとなり Private Sub Form_Unload(Cancel As Integer)のIf Me.埋め込み1(サブフォームコントロール名)の部分が示されます。メイン・サブに未入力で閉じても同じエラーとなります。
この回避はどうするといいでしょうか?
ありがとうございます。[OldValue]の事は全くしりませんでした。イベント使わずに計算が反映しますので、これは便利です。それと”フォーカス喪失時”と”フォームの読み込み解除時”のイベントの違いを教えて頂けますでしょうか(知識不足でフォームの流れがわかっていないのですみません)。
あと質問と異なるのですが、このフォームでメインにだけ入力してサブにはレコードが1件もない状態を回避したい場合、どうすると効果的でしょうか?(閉じたり、メインレコード移動時にメッセージを出したい)
Accessの仕様としては、F2、F4に金額を入力しただけではフッターの集計には反映されません。
レコード移動するとか、[Shift]+[Enter]キーを押す、レコードセレクタをクリック、・・・などなど
のレコード保存アクションを起こすと反映されます。
このレコード保存アクションを起こす前に、テキストボックスで金額を入力した直後に反映させたいなら、下記のリンク先の方法を参考にしてください。
フォームでの集計をレコード保存する前に反映させる - hatena chips
ただ、通常はそこまでしなくてもいいように思います。
サブフォームコントロールのフォーカス喪失時のイベントで合計値をチェックして合致していなかったら、イベントをキャンセルすればいいでしょう。
あとフォームの読み込み解除時でもチェックする必要があります。
サブフォームコントロール名(サブフォームを埋め込んでいるコントロールの名前)を「埋め込み1」、
サブフォームのフッターの集計テキストボックスの名前を SumF2, SumF4 と仮定した場合のサンプルコードです。
メインフォームのモジュールに記述します。
サブフォームコントロール名の確認法は下記を参考に。
サブフォームとサブフォームコントロールの違いとは? - hatena chips
hatenaさま
早速なるお知恵を授けていただき、ありがとうございます(にっこり)。
確かに!
「テキスト(CSV)形式やAccessのテーブルとしてエクスポートする」のはアリ、充分実用性がありますね。
またDB統合についても、現状ではファイルサイズが2GBを超えることは「まず、ありません」ので、
大丈夫そうです。
まずは状況を交通整理して、試してみようと思います。
(備忘録、も参考にさせていただきます♪)
今回のお知恵に感謝申し上げます(深々お辞儀)。
すみません説明不足でした。サブは下記のイメージです
F1 F2 F3 F4
摘要A ¥100 摘要B ¥50
摘要C ¥20 摘要D ¥70
このF2とF4の各合計値が合致する必要があります(サブフッターには各Sum値をテキストボックス)。金額入力ミスのチェックをしたいのです。
再更新ボタンもサブフッターに配置しており、単純に” Me.Requery” の記述だけです。
改善したいことはサブに金額入力したらすぐにサブフッターのテキストボックスに反映したいのと(再更新ボタン押さなくても)、合計値が合致していない状態でフォームを閉じたりメインに配置してます移動ボタン(新規レコード含む)で移動できない様に入力チェックが出来ないかなと思いまして。
Excel形式でエクスポートではなくテキスト(CSV)形式にする
または、
Accessのテーブルとしてエクスポートする
とかでどうでしょう。
サイズ問題に関しては、
毎日1時間ごとにバックアップをとるということですよね。
バックアップとしてどのくらい以前のデータまでさかのぼる必要があるのか不明ですが、
例えば、
本日分と前日分は毎時間分(16ファイル)
それ以前は、その日の最終分の1ファイルを残して削除
1か月以前のファイルは、月初めのファイルのみ残して削除
1年以上前のファイルはすべて削除
というように決めておけば(一例ですので実情にあわせて)
無制限にバックアップのサイズが増えることはないので
現在のストレージの価格を考えたらそれほど負担ではないと思いますが。
二つのファイルを合わせても400MBぐらいなら、統合したとしてたぶん問題ないと思います。
テーブル設計がどうなっているのか不明ですので断言はできませんが、
自分の経験でいくとそのぐらいでインデックス破損やDBのアクセス遅延はないように思います。
Accessのファイルサイズ制限の2GBに近くなったら分割を検討するのでいいかと。
MS Office Accessのサイズが2GBを超えてしまったときの対処法 | 俺的備忘録 〜なんかいろいろ〜
「その合計が合致する必要がある」とは、具体的に何と何が一致する必要があるのでしょうか。
サブフォームのフッターに表示させているF2の集計値とF4の集計値でしょうか。
「F2、F4入力後すぐに計算」とは、F2テキストボックスで入力した直後(レコード保存する前)に集計値に反映させたいということでしょうか。
とりあえず「再更新(Requery)ボタン」に設定してあるコードを提示して、こ処理でどの部分をどのように改善したいのかを説明してもらうと回答しやすいです。
まずは、どの程度の規模のシステムなのかによるでしょう。
レポート数が一桁から20ぐらいまでなら、どちらでもいいでしょう。
レポート数が多数になると、名前付きクエリにしておくと、クエリ数も増えて管理が煩雑になります。
クエリ名を工夫してレポートの関係が分かるようにするという管理法もあるにはありますが。
私の設計したシステムの場合、レポート数100以上なんてのもありますので、ほとんど埋め込みクエリにします。
ただし、複数のフォームやクエリで共通で使うクエリは名前付きクエリにすることもあります。
レポートのレコード ソースを設定する
名前付きクエリと埋め込みクエリの項目をチェックですね
どうでもいい経験談を加えると
「ACCESSが壊れた」とき、レコードソースプロパティが壊れてクエリがわからないなんてことには何度か遭遇してますね
名前付きクエリのSQLが壊れたのには遭遇したことはありません(偶々かもしれませんが)
「種別」テーブルと「商品リスト」テーブルがあってそれがコンボボックスの値集合ソースに設定されているということですか。
とりあえず一般的な設計法のサンプルです。
下記のようなテーブルを作成します。
T_種別
商品コンボボックス
名前 cmb商品
値集合ソース SELECT * FROM T_商品 WHERE 種別CD = [cmb種別];
連結列 1
列数 3
列幅 0cm;0cm
cmb商品のフォーカス取得時のイベントプロシージャを下記のように記述
すみません、追記させてください。
「\10.0.0.1\共有CSVフォルダ」が正常に表示されませんでした。
コードブロックとして再度記載します。
>> 8
じゃあ、各々のPCで取込・加工・登録が終わってから、各々でサーバ保管庫に投げておけばいいんじゃね?
返信遅れて申し訳ございません。
配列と、連想配列まわりを、ゴニョゴニョやってました。
エクセルのシートを使用しないのが、大きな目的です。
そして、配列と連想配列はすごいです。
キーは重複できないという特性を利用すると、
とんでもない速度で集計できるようになりました。
文字列処理も、連想配列とjoinを使うと負荷が減るようです。
集計と編集をして、カンマ区切りの状態にしてから、
可視で確認するために、最終状態を一気にエクセルシートに張り付けると、
瞬きする間に処理が終了していました。
EXCELは応答していません、と表示されている時間は嘘のようです。
データに間違えが無ければ、エクセルに張り付けずに、
そのまま指定の形式のファイルに出力する方法に変更します。
データがさらに大きくなると、配列に読み込むこと自体に限界が来るようですが、
自己使用の目的だけであれば、VBAは遅いという事は無いと思います。
これを、EXCELと呼べるか微妙ではありますが、とにかくハイパフォーマンスです。
それ以上踏み込むか、とどまるかは目的と趣味の問題ですね。
ちょっとどの程度まで理解しておられるか分かりませんので、余談になるかもしれません。
会社のサーバー(ファイルサーバーか共有フォルダ、のはず)に置き、ということなので、同じネットワーク上にあるPCからでしたら、共有フォルダのアドレスは変わらないはずです。
例えば「10.0.0.1」というIPアドレスにある「共有CSVフォルダ」という共有フォルダの場合、どのPCからアクセスしてもアドレスは「\10.0.0.1\共有CSVフォルダ」ですよね。
※探し方は参考リンクを書いて頂いてますのでそちらを参照してください
ですの以下のように設定すればいいのではないでしょうか?
DoCmd.TransferText acImportDelim, "my_imp", "注文取込", "\10.0.0.1\共有CSVフォルダ\中略\注文.csv", False, ""
または
folderName = "\10.0.0.1\共有CSVフォルダ"
ただしAccessのプログラムをネットワークフォルダなどに置き、同時に複数人が使用すると不具合が発生します。例えばプログラムを使って同時に取り込み作業を行った場合、データベースに同時に書き込みが発生してしまい、壊れやすくなります。
参考:https://sys-daddy.com/multi-user-setting-of-access/
そこで「データベース分割」などが必要になってきます。
(でも書き込みを見ると、ある程度できているようですね)
できました、ありがとうございます
メインデータ.accdbはサーバーに置いてあり、作業用.accdbは各クライアントPCにおいてあるということでしょうか。
複数ユーザーでデータを共有する場合は、データベースの分割は必須ですので、しっかり学習しておきましょう。WEB検索すれば解説ページは多数見つかります。
access データベースの分割 - Google 検索
はい、そうです。
アクセスの知識というよりサーバー上のファイルを共有する場合の前提知識ですね。
リンクテーブルにする場合もネットワークパス経由でリンクすることになります。
マスターテーブルと、取り込んだ注文.csvを加工して作成した注文データテーブルは、メインデータ.accdbに入ってり、テーブルのみのファイルになっています。他の作業用.accdbファイルではそのテーブルをリンクさせています。
どういうことでしょうか?初学者なものでデータベース分割、リンクテーブルはよく理解しておりません。
これをどうにか取得して、変数に入れるということですか?
これってそもそもデータベース分割とリンクテーブルをきちんとやっているの?
Report_Closeイベントはキャンセルできません。
自動生成されたイベントプロシージャにCancel引数がないイベントはキャンセルできません。
読み込み解除時(Report_Unload)イベントならキャンセルできます。
ネットワークパス(UNCパス)を指定すればいいでしょう。
Windowsネットワークパスについて – 名古屋のパソコン入替・パソコン設定・キッティング作業業務
完全ガイド - Windows 10でネットワークドライブのパスを見つける方法 - MiniTool
一般的には下記のような書式になります。
現在は自身のPCのデスクトップ上のフォルダの中に注文.csvがあります。
このフォルダを会社のサーバーに移動させます。
Accessの入っている会社のPCならどれからでもこのコードが動くように、PCからそのサーバー上のフォルダまでのパスを取得したいです。
ちなみに、C:¥Users¥中略の部分を変数にするコードは下記のようになります。
ただし、質問のコードでエラーがでるならこのコードでもエラーはでます。
やっていることは同じことなので。
どのPCからでもということですが、これらのPCは同じローカルネットワーク内にあるということですか。
C:¥Users¥中略 というフォルダーはローカルネットワーク上の特定のPCのフォルダーということですか。
「どのPCからでもこのフォルダを扱えるよう」ということを上記の点も含めていもう少し詳しく説明してもらえますか。
自身のPC内のC:¥Users¥中略フォルダーでそこに注文.csvが存在するなら、そのコードではエラーにはならないと思いますが。
前の質問の時もそうでしたが、こちらの質問に対する応答がないですね。
結局、何がしたいのか、何が問題なのか不明のまま議論が深まらずに終了するということになってます。
配列をどうしたいのか、それが不明瞭のままです。これを明確にしてください。
レコードセットのデータをエクセルに出力するというのが目的でしょうか。
だとしたら、skさんの回答の CopyFromRecordset を使う方法で簡単に実現できます。
連想配列を使う必要もないです。
hatenaさん、すみませんでした。
連想配列の存在を知らなかったので、読み飛ばしていました。
分からないところが山もり状態ですが、
避けて通るとEXCELに応答していませんと表示されるのを、
数秒間眺めることになるので、なんとか理解します。
放置すると、動かなくなる日は近いはずなので。
ここをクリアしても、また限界が現れると思いますが、
配列周りの処理方法は、次の段階で役立つと思うので、取り組んでみます。
そもそもそのプロシージャはどのファイルのどのモジュールに記述されているのか( Access データベースファイル上の標準モジュール、Excelマクロ有効ブックの標準モジュールなど)。
取得したレコードセット(の各フィールドの名前と値)をどこに、どのような形で貼り付けようとしているのか。
以上の前提についてのご説明がない限り、何を問題視されているのか不明瞭なままです。
例えば「ある accdb ファイル上に存在するテーブルから[sptx]、[sbj]、[pymt]、[fwd]、[dat]の 5 つのフィールドを選択し、全てのレコードを[id]の昇順に並べ替えた結果を取得して、新規ブック上のワークシートに複写するプロシージャを Excel マクロ有効ブック上の標準モジュールに作成しようとしている」といった場合であれば、2次元配列に拘らずとも、Excel.Range オブジェクトの CopyFromRecordset メソッドを使用する、といった方法もあるでしょう。
そのコードを前の回答で提示したつもりですが。
どちらにしても、
2 次元配列をどのように利用しようとしているか。
レコードセットではだめなのか、その辺の説明がないと、
これ以上、議論のしようがないように思います。
怪しい民間療法みたいな街角勉強会に引っかかっているんじゃないかしら?accessのようなデータベースがまだ発明されていない時代に、流行っていた思想は廃れたはずですが。
まず、データと一緒に、フィールド名を格納することはできなかったようでした。
認識不足でした。
ACCESSのテーブルの、何番目のフィールドか調べたいわけではありません。
レコードセットは、フィールド名でデータを取り出せるので、
事前に、フィールドの並び順をカウントしておけば良いのですが、
よく単純な数え間違えが起こります。
何番目に、どのフィールドをおいて呼び出したのか、
確認するコードを加えれば済む話ではあります。
なのですが、フィールド名でデータを特定するのがベストなので、できればそうしたいということです。
と言っても、データを整理して一括で貼り付けないと、負荷はほとんど変わりません。
この処理段階の方が、何倍もミスを犯しやすいので、取るに足らないこだわりかもしれないです。
私はデザイン、レイアウトに力を入れてますよ。
ただし凝ったデザインにはしません。
シンプルでユーザーが使いやすいUIになるように意識してます。
例えば、下記とか。
入力必須項目のある入力フォームのUIを考察する - hatena chips
白背景にテキストボックスの羅列が使いにくいとは思いませんが、ラベルはあった方かいいでしょうね。
下記とか。
Accessフォームでおしゃれなデザインを作成 | 工場エンジニアのAccessスキル
ACCESS フォーム作成サンプル - たすけてACCESS
レコードセットは配列の上位互換だと思いますが、なぜ配列で処理する必要があるのでしょう?レコードセットならフィールド名でデータを取り出せます。どのような目的で配列にするのでしょうか。
やるとするなら、連想配列(Dictionary)にフィールド名と何列目かを格納して、参照することになるかな?
具体的にどのような目的から 2 次元配列を用いようとされているのでしょうか。
少なくとも、上記のコードをそのまま記述すればコンパイルエラーが発生します。
2 次元配列を使用する目的が不明ですので、今のところ評価のしようがありません。
ありがとうございます!頂いた情報をよく読み、チャレンジしてみます。
とりあえずで回答しますが、孫フォルダを対象にしたいなら
Sub フォルダ名が売上データ○○になっているフォルダを取得する()
Dim FSO As New FileSystemObject
Dim 検索フォルダ As Folder
Set 検索フォルダ = FSO.GetFolder("C:\Users\○○\Desktop\検索フォルダ")
Dim 該当フォルダ As Folder
Dim フォルダ候補 As Folder
For Each フォルダ候補 In 検索フォルダ.SubFolders
If フォルダ候補.Name Like "売上データ*" Then
Set 該当フォルダ = フォルダ候補
Exit For
ElseIf
'フォルダ候補(子フォルダ)を親として同じことをする
End If
Next
If Not 該当フォルダ Is Nothing Then
MsgBox "該当フォルダのパス:" & 該当フォルダ.Path
End If
End Sub
とするだけですね
それぞれの変数がどのように使われるのか理解して、必要に応じて必要な変数を用意してください
ただし、別階層のフォルダを対象とするということは、同名のフォルダがある可能性が否定できないので、処理の順番によっては望んだ結果にならない場合もあります。また、「見つかったものは全て」というのが望みの場合もあるでしょう。仕様を明確にしましょう
最初の"T_入出庫一覧"テーブルを正規化すると下記のようになります。
T_区分マスター
正規化についてはWEB検索すれば解説ページは多数見つかりますので、わかりやすそうな所をいろいろ見て理解を深めてください。
とりあえず下記を紹介しておきます。
正規化とは - もう一度学ぶMS-Access
正規化の実例 - もう一度学ぶMS-Access