マルチユーザーアクセスを前提としているのであれば、少なくとも
SQL Server のテーブルに格納されている全てのレコードを、 編集目的のためにまるごと呼び出して表示するような処理は
極力避けた方がよいでしょう。
(トランザクションや排他ロックなどの制御を行なう必要があるなら
なおのこと)
Option Compare Database
Option Explicit
Private adoCn As ADODB.Connection
Private adoRs As ADODB.Recordset
Private Const ServerName As String = "ServerName"
Private Const UserName As String = "UserName"
Private Const Password As String = "PassWord"
Private Const CatalogName As String = "DatabaseName"
Private Const TableName As String = "TableName"
'フォームの[開く時]イベント
Private Sub Form_Open(Cancel As Integer)
On Error GoTo Err_Form_Open
Me.Painting = False
Set adoCn = New ADODB.Connection
With adoCn
'SQL Server Native Client をプロバイダーとする場合
.ConnectionString = "Provider=SQLNCLI11;" & _
"Data Source=" & ServerName & ";" & _
"User Id=" & UserName & ";" & _
"Password=" & Password & ";" & _
"Initial Catalog=" & CatalogName & ";" & _
"DataTypeCompatibility=80"
.CursorLocation = adUseClient
.CommandTimeout = 1
.Open
End With
Dim strSQL As String
strSQL = "SELECT * FROM [dbo].[" & TableName & "] ORDER BY [ID];"
Set adoRs = New ADODB.Recordset
With adoRs
Set .ActiveConnection = adoCn
.Source = strSQL
.CursorLocation = adUseClient
.CursorType = adOpenKeyset
.LockType = adLockOptimistic
.Open
End With
Set Me.Recordset = adoRs
Exit_Form_Open:
Me.Painting = True
Exit Sub
Err_Form_Open:
Dim ErrText As String
ErrText = "実行時エラー " & Err.Number & ": " & Err.Description
Debug.Print ErrText
MsgBox ErrText, vbCritical, Me.Name & ".Form_Open"
Call ReleaseObjects
Cancel = True
End Sub
'フォームの[読み込み解除時]イベント
Private Sub Form_Unload(Cancel As Integer)
Call ReleaseObjects
End Sub
'コマンドボタン[cmdExecFilter]の[クリック時]イベント
Private Sub cmdExecFilter_Click()
'編集中のカレントレコードを保存する
If Me.Dirty = True Then
DoCmd.RunCommand acCmdSaveRecord
End If
Dim strCriteria As String
'非連結テキストボックス[txtFilter]の値が
'数値データに変換可能である場合
If IsNumeric(Me![txtFilter].Value) Then
'フィールド[ID]の値が、非連結テキストボックス[txtFilter]の値を
'整数値に変換した結果と等しいレコードを抽出する条件式
strCriteria = "[ID]=" & CLng(Me![txtFilter].Value)
End If
'フィルターを適用
adoRs.Filter = strCriteria
'レコードセットを再連結
Me.Painting = False
Set Me.Recordset = Nothing
Set Me.Recordset = adoRs
Me.Painting = True
End Sub
'コマンドボタン[cmdClose]の[クリック時]イベント
Private Sub cmdClose_Click()
'このフォームを閉じる
DoCmd.Close acForm, Me.Name, acSaveNo
End Sub
'オブジェクトの解放処理
Private Sub ReleaseObjects()
On Error Resume Next
If Not adoRs Is Nothing Then
If adoRs.State = adStateOpen Then
adoRs.Close
End If
Set adoRs = Nothing
End If
If Not adoCn Is Nothing Then
If adoCn.State = adStateOpen Then
adoCn.Close
End If
Set adoCn = Nothing
End If
End Sub
Private Sub Form_Current()
With Me.Parent.Form
!cmb_分類 = Me!分類
!txb_コード = Me!コード
!txb_品名 = Me!品名
!cmb_単位 = Me!単位
!txb_巾 = Me!巾
!txb_長さ = Me!長さ
!txb_登録日 = Me!登録日
!txb_更新日 = Me!更新日
!txb_単価 = Me!単価
!txb_改訂前単価 = Me!改訂前単価
End With
End Sub
次にメインフォームの各コマンドボタンのクリック時のイベントプロシージャを下記のように記述します。
Private Sub 修正_Click()
With Me.F_売上.Form
!分類 = Me!cmb_分類
!コード = Me!txb_コード
!品名 = Me!txb_品名
!単位 = Me!cmb_単位
!巾 = Me!txb_巾
!長さ = Me!txb_長さ
!登録日 = Me!txb_登録日
!更新日 = Me!txb_更新日
!単価 = Me!txb_単価
!改訂前単価 = Me!txb_改訂前単価
.Refresh 'レコードソースのテーブルに反映
End With
End Sub
Private Sub 削除_Click()
With Me.F_売上.Form
'サブフォームが新規レコードでなければ、カレントレコードを削除
If Not .NewRecord Then .Recordset.Delete
End With
End Sub
Public Function Tab2Spaces(s As String, n As Long)
Dim lines: lines = Split(s, vbLf)
Dim l
For Each l In lines
Dim a: a = Split(l, vbTab)
Tab2Spaces = RTrim(Tab2Spaces) & vbLf
Dim i
For Each i In a
Dim num As Long
num = n - LenB(StrConv(i, vbFromUnicode))
If num < 0 Then num = 0
Tab2Spaces = Tab2Spaces & i & Space(num)
Next
Next
Tab2Spaces = Mid(Tab2Spaces, 2)
End Function
下記のようにフィールド欄に式を設定して、抽出条件をTrueにしたらどうでしょう。
IIf([Forms]![Form名]![cmb1] is null , True, [forms]![form名]![cmb1]=[フィールド名])
他のフィールドも同様に設定してください。
ご回答ありがとうございます。
ご指摘の件、確認して理解いたしました。
データにNull値がないようにするセーフティーネットはつけていなかったので、
Nullのデータがある場合には検索ができなくなりますね。
対処法はあるのでしょうか。
like iif([forms]![form名]![cmb1] is null , '*', [forms]![form名]![cmb1])
これだと、Null値のレコードは抽出されません。
テーブルに Nullのレコードがなければいいですが。
Office VBA Reference: Bind a form to an ADO Recordset
・そのレコードセットが ADO を介して更新可能であること。
・そのレコードセットに一意なインデックス(テーブルの主キーなど)が含まれていること。
基本的には上記の条件を満たしているか否か次第。
なので、レコードセットを開く際にどのような SQL 文を実行しているかも
無関係ではありません。
ADO.Recordset オブジェクトと連結されたフォームにおいて、
そのフォームの Recordset プロパティを介して Filter プロパティを
設定するのは無効です。
そのテーブルやフォームの使用目的によるとしか。
マルチユーザーアクセスを前提としているのであれば、少なくとも
SQL Server のテーブルに格納されている全てのレコードを、
編集目的のためにまるごと呼び出して表示するような処理は
極力避けた方がよいでしょう。
(トランザクションや排他ロックなどの制御を行なう必要があるなら
なおのこと)
とりあえずの例としては、上記のようなコードが挙げられますが。
自己解決したみたいです。
系を使えばよいのですね。
なかなか、回答が付かないですね。
私自身は、SQL Serverでの経験がないので、適切な回答はできないので、
感想的な回答ですので、参考程度に。
そのようなソースのリンクがあれば紹介してもらえますか。
個人的には、リンクテーブルだから遅いのではなく、リンクテーブルと連結したフォームだと、常に更新可能なレコードセットとしてサーバーデータと常時接続された状態だから、遅いのだと思います。
ですので、ADOのレコードセットを開いて、フォームと連結させたら、結局、上記と同じ状態なので、速度的にはたいしてかわらないと推測しています。
もし、リンクテーブルと連結したフォームより、ADOのレコードセットと連結したフォームの方が速度的に有利であるという信頼できるソースがあるなら知りたいです。
下記によると要件さえ満たしいれば、更新可能になるようです。
ADO レコードセットにフォームを連結します | Microsoft Docs
フォームのそのようなプロパティはレコードソースで接続していることが前提のようで、Recordsetプロパティで連結した場合はほとんど使えないようです。RecordsetのFilterプロパティを使うか、SQLでレコードセットを再取得して、それと連結させるという処理が必要なようです。
ということで、あくまで、想像ですが、
連結フォームのような使い勝手は難しいと思います。
結局、速度改善したいなら、
パススルークエリで、必要なデータのみ取得して、これは閲覧のみ、更新する場合は、別に非連結フォームで更新したものを、更新クエリ、あるいは追加クエリでデータ更新することになるのではと思っています。
いつも教えていただいてありがとうございます。勉強させていただいています。
見積書を作るにあたって、材料マスタのデータを抽出して使いたいのですが、色々変更などを言われていて
レイアウトなどが変わりそうなので、また改めて質問させてください。
引き続き勉強していきます。
またよろしくお願いします。
hatena 様
お世話になります。
2つ目にお教えいただいたようにいたしましたら、うまくいきました。
ありがとうございました。
今後ともよろしくお願いいたします。
現状の1列表示のレポートを修正するなら、
レポートをデザインビューで開いて、[ページ設定]の[レイアウト]で、列数を3に設定してサイズをラベルのサイズに設定してください。
[作成]-[レポート]-[宛名ラベル]をクリックして「宛名ラベルウィザード」でご使用の用紙の品番を探してみてください。
あれば、そのままウィザードを進めていけばできます。
なければ、ラベルサイズが近いものを選択するか、[ユーザー定義ラベル]でラベルサイズや余白、列数を自由に設定できます。
一つのフォームの中でデータを書き込む場合、たとえば
と記述します。
Me
は自分自身を表すキーワードなので、異なるフォームを相手にする場合はこれを書き換えてあげますMe
フォームを直接指定する記述Forms!フォーム名
としてあげればいいですこれとは別に、データ構造の問題もあると思うので、必要であれば情報を追記してください
たとえば、
・見積に材料マスタのデータを結び付ける必要がある場合
・参考データとして既存のデータを呼び出したいだけの場合
やりたいことによってデータ構造(と、制御処理)が変わってきます
わざわざ調べていただき、ありがとうございます。
やっぱりエラーがでるので、フォームをコピーしてから開いてみたらエラーが出ませんでした。
理由はわかりませんが、ありがとうございました。
登録ボタンと修正ボタンの方も、教えていただいたコードを使ってできるようになりました。
ありがとうございます。また一歩前に進みました。
やりたいことはあって、書籍やYoutubeやネットを色々みて勉強しているつもりなのですが、
知識が追い付かないので、なかなか思うようにできません…
お聞きしてばかりで本当に申し訳ないです。
また教えてください。よろしくお願いします。
当方のサンプルで確認してみましたが、
でエラーがでることはないですね。
ファイルの破損が疑われますので、修復・最適化をしてみるか、下記で紹介しているリフレッシュを試してみてください。
AccessのDBファイルを長期的に安定して使用するには - hatena chips
下記で紹介している方法が使えると思います。
レコード入力時に「保存」コマンドボタンでのみレコード保存できるようにする - hatena chips
いつも大変お世話になっています。教えていただきありがとうございました。
分割フォームは、作ってみたのですがうまくいかなくて、今回のような形で作ってみました。
でも、スキルが必要とのことだったので、こちらはいつかちゃんとできるようになってから再度チャレンジするとして、
もう一度分割フォームで作成してみました。
それで、一応形はできたと思うのですが、少し質問させてください。
1.フォームをポップアップにして、開いた時のサイズを指定したいのですが、この分割フォームで作ったものを
他のものと同じように設定したところ、下記のようなエラーがでてしまいます。
コードは、「開く時」のところに
Private Sub Form_Open(Cancel As Integer)
DoCmd.SelectObject acForm, Me.Name
DoCmd.MoveSize , , 13000, 9200
End Sub
と入れていました。エラーが出るので、指定せずに開くと画面いっぱいになってしまって、使いづらいです…
2.登録する時と修正する時に、登録ボタンと修正ボタンを押さないとできないようにすることはできますか。
(これができないような気がして、サブフォームで非連結のフォームを作ってみました)
いつも教えていただいて申し訳ありません。
よろしくお願いします。
推測ですが、Windowsの「カスタムスケーリング」の設定がPCによって異なっているのではないでしょうか。
「カスタムスケーリング」の設定を同じにしてみてください。
【Windows10】画面の解像度を変えずに文字だけを拡大する方法 - 特選街web
まず、回答の前にひとつ提案ですが、
現状のサブフォームのレコードソースのテーブルから、分割フォームを作成すれば、単票フォームの下にデータシートビューが表示されて、データシートでレコードを選択して、単票フォームの方でそのレコードの編集ができるようになります。
いっさいVBAなしで、見た目は現状とほぼ同じものが作成できます。
分割フォーム(単票フォームとデータシートを同時に表示)の作成と使い方 | Access 2016 | 初心者のためのOffice講座
上記を検討されることをお勧めします。
現状のサブフォーム形式で行く場合、かなりのスキルを必要とします。
本題
サブフォーム形式にチャレンジするということなら、
まずは、サブフォームのレコード移動時のイベントプロシージャを下記のように記述します。
次にメインフォームの各コマンドボタンのクリック時のイベントプロシージャを下記のように記述します。
このような設計にした場合、
現状の登録ボタンとクリアボタンの処理は再検討する必要があると思います。
登録ボタンは、サブフォームが新規レコードのときのみ有効にするとか。
また、クリアボタンが必要かどうかも検討する必要があります。
あともう一点お聞きさせてください。
先ほど、自分のパソコン(デスクトップPC)とは違うパソコン(ノートPC)で作ったデータを見せてもらったら、
レイアウトが少しずれていました。(線が重なっていたり離れていたり、文字の位置がずれていたり)
自分のパソコンからはきれいに見えていたのでちょっとショックだったのですが、
これは、何かAccessの設定の問題でしょうか…
たびたびお世話になっています。
メインフォームは非連結です。
このような感じです。
コードは、
Option Compare Database
Private Sub btn_クリア_Click()
Me!cmb_分類 = ""
Me!txb_コード = ""
Me!txb_品名 = ""
Me!cmb_単位 = ""
Me!txb_巾 = ""
Me!txb_長さ = ""
Me!txb_登録日 = ""
Me!txb_更新日 = ""
Me!txb_単価 = ""
Me!txb_改訂前単価 = ""
End Sub
Private Sub btn_登録_Click()
DoCmd.SetWarnings False
DoCmd.OpenQuery "Q_追加T_原材料テーブル"
DoCmd.SetWarnings True
Me!cmb_分類 = Null
Me!txb_コード = Null
Me!txb_品名 = Null
Me!cmb_単位 = Null
Me!txb_巾 = Null
Me!txb_長さ = Null
Me!txb_登録日 = Null
Me!txb_更新日 = Null
Me!txb_単価 = Null
Me!txb_改訂前単価 = Null
DoCmd.GoToControl "cmb_分類"
Me.sub_原材料.Form.Requery
End Sub
Private Sub btn_閉じる_Click()
DoCmd.Close acForm, Me.Name
End Sub
Private Sub Form_AfterUpdate()
End Sub
Private Sub Form_Open(Cancel As Integer)
DoCmd.SelectObject acForm, Me.Name
DoCmd.MoveSize , , 13000, 9200
End Sub
このようになっているのですが、これで大丈夫でしょうか。
よろしくお願いします。
メインフォームは連結ですか、非連結ですか。
非連結の場合、現状のメインフォームのコードを提示してもらえますか。
サブフォームにもコードがあるなら、それも提示してください。
できました!
いつもありがとうございます。もっと勉強します。
サブフォームを再読み込みすればいいでしょう。
メインフォームの登録ボタンのプロシージャの最後に、下記を追加してみてください。
サブフォームコントロール名については下記を参照ください。
サブフォームとサブフォームコントロールの違いとは? - hatena chips
hatenaさん、名前なしさん
ご回答ありがとうございます。
連絡遅くなりまして申し訳ございません。
入力/編集用として考えています。
hatenaさんのアドバイスの条件付き書式を行いましたが、ちらつきが気になってしまいました。
試行錯誤の結果、名前なしさんの考え方を採用しました。
誠にありがとうございました。
今後ともよろしくお願いいたします。
下記でどうでしょう。
?
は1 つの文字を表します。そのほか、いろいろな指定法がありますので、下記を参照されるといいでしょう。
Like 演算子
A.在庫リストをリンクテーブルにしておいて、中身だけ追加・削除を行う
B.エクスポート後PowerShellで在庫リストを削除・リネームする
リスト(テーブル)のような「箱」は頻繁に作成・削除するようなものじゃないので中身だけ操作する仕組みを考えたほうが良いと思います。リンクテーブルにしてしまえばそれほど難しい処理は必要ないでしょう
ただし、リンクテーブルにすると余計なフィールドが追加されたり、ローカルのテーブルで使えてた一部の機能が使えなかったりと癖があるので注意も必要です
※PowerShellによるShare pointの操作はできるという資料をWeb検索から見ているだけでhirotonはまともにやったことはありません
ありがとうございます
結局Sendkeys(Wscript.Shellの方)を使ってペイントに張り付けて名前を付けて保存の処理を一括自動でやる形を取りました
実際バイナリを取ってみたところ元がbmpなのに「イメージ」や「DIB」とは書かれていたのですが
「BM」と書かれていた所が見つからなかったので断念しました
例えば、質問文のテーブルを料理別材料明細テーブルとする。このテーブルから下記のクエリを作る。とりあえず、このクエリをもとに、あれこれやってみるのはどうだろう。
とはいえ、なんだかんだ使っているうちに不具合が出てくるはずなので、諸々手当てが大変にはなる。
式1:◼◼は、式1:DCount(“材料”,”料理別材料明細テーブル”,”[料理別材料明細テーブル].[料理]=‘“ & [料理別材料明細テーブル].[料理] & “‘And [料理別材料明細テーブル].[材料]<=‘“ & [料理別材料明細テーブル].[材料] & “‘“)
式2:◼◼は、式2:IIf([式1]=1,[料理別材料明細テーブル].[料理],””)
やるべき事は慣れる事だと思う。慣れてくれば、逆に明細表示のほうが見やすくなる。明細表示が操作出来ない、いつも見やすくして欲しい、もっと見やすくして欲しい、なんて事になったら、笑うに笑えない。
その帳票フォームは閲覧専用ということでしょうか。
閲覧専用なら、レポートのレポートビュー表示にするのが簡単です。
入力/編集もしたいのなら、条件付き書式で文字色を背景色と同じにするという方法になりますが、重い処理になりますので、ちらついたりして、あまり使いやすいものにはならないと思います。
保存の仕方が一定ならヘッダも同じ内容になるはずなのでサンプルを取って調整してあげれば上の回答の内容でいけるんですが
ちなみにhirotonが試してみたらこうなってました
なので、たとえば
bytImage() = objRecordSet.Fields("IMAGE").GetChunk(87, nSize)
としてあげれば保存したbmpファイルがそのまま開けました
変動するなら、「バイナリ読んでBMの文字が出るまで」とかになるんでしょうか。バイナリの読み方はわかりませんが
クリップボード経由するならPowerShellにはクリップボードの画像を保存する方法があるようなので、コマンド(スクリプトファイル)を用意しておいて叩くとか、クリップボードに画像が保存されたら自動でファイルに保存するアプリを使うとかですかねぇ
ありがとうございます
実際acOLECopy後にペイント起動して貼り付けをした後に名前つけて保存すれば手動でファイル保存すればできているのですが
これが1万件あるのであとちょっとの部分(クリップボード内をパス指定ファイル保存)さえ自動化できれば…と思い何とかもがいていたところです
ExcelだとVBAで空シートに貼り付け→グラフ化→グラフのイメージ化という手段が取れてるので
Accessでも何かできればとは思ったのですがどうしてもクリップボード経由でなく直にOLEを変換する方向しか見つからず
それもこのDBだとヘッダが違うらしく八方塞がりになっていました…
一筋縄ではいかないようですね
ただ、Web検索でACCESSでの情報がないというわけでもないようです
Yahoo Japan
画像を読み込む(GetChunkメソッド)(モーグ)
”OLEオブジェクトフィールドのイメージをBMPファイルに書き出す”の覚え書き (Access2003)(とくさんブログさん)
私もいろいろ検索してみましたが見つかりませんでした。
手作業で一つずつ保存するか、その手順をRPAツールなどで自動化するぐらいでしょうか。
すみません、前提が抜けていました
既にOLEオブジェクト型埋め込みで作られているDBがあり、
それを作り変えるための前提としてファイルを保存して取り出しをしたかったのです
なので取り出しの段階ではDB構造は変更できません
連結オブジェクトフレームということは、画像データはOLEオブジェクト型のフィールドに保存されているということですね。
OLEオブジェクト型というのは、古い技術ですので、重い、サイズが大きくなる、扱いにくい、などデメリットが多いので、
Access2007以降なら、添付ファイル型に格納するのがデフォです。
添付ファイル型なら下記が参考になるでしょう。
■T'sWare Access Tips #492 ~添付ファイルをディスクに保存するには?~
hatena様
こんな質問にご丁寧に回答ありがとうございました。
そのフォームでしか使わない処理なら、フォームモジュールに記述するのがいいですね。
他のフォームでも使う場合があるなら、標準モジュールですね。その場合、前の回答に示した通り、Me を CodeContextObject に変更すればいいです。
言葉足らずで申し訳ありません。標準モジュールに記載することしか考えられていませんでしたが、
同一のフォームモジュールに記載して各テキストボックスのAfterUpdateからcallで呼び出したほうが良いですね。
動作を確認することができました。まったく気が付けませんでした。
これが当初にやりたかった事と一番マッチしております。大変勉強になりました。
本当に有難うございました。今後とも何卒よろしくお願いいたします。
『モジュールに記載したコード』というのはどのモジュールでしょうか。フォームモジュールでしょうか。そとれとも、標準モジュールでしょうか。
フォームモジュールなら、
あるいは、
でいいはずですが、それでだめでしたか。
標準モジュールだと
Me
は使えないので、その部分はCodeContextObject
に変更するなどする必要があります。もう1点ご教示頂きたくよろしくお願いします。
上記と同じ条件で連結フォーム上の同一レコードに複数のテキストボックスボックスがあり、
上記の Me!氏名 = Me!名字 & Me!名前 の処理と同じ処理をテキストボックスすべてに実施したいのです。
実際は処理部分がもっと長いため、AfterUpdate()のプロシージャ内にモジュールに記載したコードを
callで呼び出したいと考えていますが、その場合にはどのように記載すれば良いでしょうか。
よろしくお願いいたします。
全角文字が含まれていても揃うように修正しました。
フォントは等幅フォントにしてください。
半角文字が前提です。全角文字が含まれるとずれます。
第2引数で指定した数字より長い単語があるとずれまず。