★先ほど間違えて、新しく作成で投稿しませんでした(すみません)同じ内容ですが投稿します
先日 strSQL = strSQL & " WHERE " & Me.Filter・・・を教えて頂いた者です。
関連のフォームでSQL実行させたい事があるのですが分からないことがでてきました。先日の応用が出来なくて、もどかしいです・・・。やりたい事は部品現品は入荷済みで納品書が未達の分を抽出するフォームを作成しています。そのフォームには仕入先、入荷日、直送先等の抽出条件コンボボックスがあり前回同様 strFilter = mid(strFilter, 6) で抽出出来ています。レコードにはチェックボックスがありチェックYesにしたものだけに後日届いた納品書の日付をフォーム上のテキストボックスから代入して一括入力させたいのです(レコードに1件づつ直接日付入力も可能)。レコード1件づつチェックYesにする事も可能ですがFilter抽出した分にその一括処理をする更新クエリをしているのですが、現状抽出条件毎にクエリを複数作成しています。それを集約させたいのでWHERE " & Me.Filterでしようと思っていたのですが、いざ記述しようとするとどうやって?となってしまったのです。更新クエリのSQLビューは下記です。
UPDATE T納品書まだ分 SET T納品書まだ分.チェック1 = Yes
WHERE (((T納品書まだ分.納品書入り日) Is Null) AND ((T納品書まだ分.発注先ID)=[Forms]![F納品書未達処理]![cb発注先選択]));
この[cb発注先選択]部を Me.Filter にしたいのです。T納品書まだ分.納品書入り日) Is Nullの後にどうコードをつないだらいいのかが分からなくて。一括でチェックYes処理はフォーム上のボタンにDoCmd.OpenQuery・・・で処理させてましたがSQL勉強中なのでそれで処理させようと思ったのですがやり方が分からないのが現状です。
最終的にはチェックYes分に納品書日付を一括代入するボタンもSQL(更新)で処理させたいです。
またこの更新SQLの実行もVBAでどう実行させたらいいのかも分からなくて。
応用力がなく情けないのですが、アドバイスを宜しくお願いします。
通報 ...
フォームでフィルターを使いつつ、フォームに読み込んでいるデータを扱いたいのなら、DAOとか使ったほうが楽だと思います
フォームに表示されているレコードのチェックボックスを全てYesにする
いろいろ追加
SQLを発行するメリットが見つからないので、知識レベルの話としてになりますが、
フォームのボタンを押してSQLを実行するならば、
このクエリの実行で動作上の不具合はないですよね?
「処理させてましたが」「やり方が分からない」
「処理させていた」のなら「出来ている」のでは?
Me.Filter
の中にはということで、
[cb発注先選択]
以外の情報が含まれている可能性があります。この中(Me.Filter
)から[cb発注先選択]
だけの情報を抜き取りたいということであれば、それは普通やらないです。[cb発注先選択]
の情報はそのままMe!cb発注先選択
で参照できるので、わざわざMe.Filter
から抜き出したりしませんフィルターを何かの手法で直接操作して、その状態でフィルター文字列から何かの情報を取得したいというのは方向性が逆ですね。元となる個別の情報を個別に扱えるような設計を考えましょう
動的にSQLを生成するなら、そのままSQLを実行するコマンドがあります
DoCmd.RunSQL メソッド (Access)
もっといえば、VBAで処理をするならDoCmd オブジェクト (Access)はあまり使う場面がありません。たいていは、より直接的に扱える手法が用意されています。クエリ、SQLの実行であればDatabase.Execute メソッド (DAO)が使いやすいと思います
使用例
納品書日付に入力する値もフォームのテキストボックスで事前に入力しておけば、そのテキストボックスを参照してSQLの文字列に入れ込めばいいですね
改めてですが、フォームに読み込んだデータをフォーム上の作業で更新するならDAO処理のほうが楽ですね。それはそれとして、VBAでSQLを動的に扱うことはあるので、それ用のケーススタディということで
やりたいことを整理すると下記のようなことですか。
帳票フォームのヘッダーに条件用の複数のテキストボックスがあり、その値によりフォームにフィルターをかけている。
VBAで抽出条件式を生成してそれをFilterプロパティに設定している。
表示されているレコードのうち、納品書入り日が未入力(Null)のレコードのチェックボックスにチェックを入れたいということですか。
SQLで更新するなら既に出ていますが、DoCmd.RunSQL か CurrentDb.Execute を使います。
下記でどうでしょう。
フォームのレコードセットをループで回して直接更新してもいいです。(hirotonさんの回答)
フォームのレコードセットを使って更新する場合のメリットは、
抽出条件などは考えずに機械的にコーディングできる点。
デメリットは、SQLでの更新よりは処理が遅い点。
ただし、体感できる差がでるのは、よほど件数が多いときです。
下記で処理速度の検証をしていますのでご参考に。
フォームの Recordset, RecorsetClone, RecordSet.Clone の違いとは? - hatena chips
いまさら改めて見てですけど、「
cmdSQLExecute_Click
」のって、
Rnd()
がキャッシュ使われて一度しか乱数取得処理入らないんで、ほかのDAO処理のrs!F1 = Rnd
とやってること違いますねあら、そうですね。
となると、RecordsetCloneとSQLの差は縮まりそうですね。あるいは差はないかも。
要検証ですね。時間がとれたらやってみます。
ご指摘ありがとうございます。
hirotonさん hatenaさん ありがとうございます。まずはベタな方法ですが下記で記述しました。
Dim strSQL As String
strSQL =
"UPDATE Q納品書未達F用Rs SET check = Yes " &
"WHERE 納品書入り日 Is Null AND (" & Me.Filter & ");"
DoCmd.SetWarnings False
DoCmd.RunSQL strSQL
DoCmd.SetWarnings True
Me.Refresh
これで一応更新処理はできました。次にDAOでもやってみようと思ってます。それで大変初歩的な質問なのですが先頭に下記を記述でいいでしょうか? これの後にアドバイス頂きましたコードを記述でいいのでしょうか?
'変数宣言
Dim db As DAO.Database
Dim rs As DAO.Recordset
'現在開いているデータベースに接続する。
Set db = CurrentDb
あとこれも初歩的な質問ですが "WHERE 納品書入り日 Is Null AND (" & Me.Filter & ");" のANDの後は()が必要なのですね? 勉強不足ですみません。
比較演算子には優先順位があり、ORよりANDの方が優先度が高いです。
ですので、FilterにOR演算子があった場合、意図せぬ結果になる場合があります。
例えば、Filterが A = 1 OR B = 2 の場合、()がないと、
納品書入り日 Is Null AND A = 1 OR B = 2
となり、これは
(納品書入り日 Is Null AND A = 1) OR B = 2
と解釈されます。
意図している下記とは結果が異なります。
納品書入り日 Is Null AND (A = 1 OR B = 2)
DAOでSQLを実行するという意味なら、変数宣言は不要です。
CurrentDb はAccessファイルを開いた時点で自動で宣言されてますので。
つまり、下記でOKです。
DAOのレコードセットを利用して更新するということなら、フォームを開いた時点で、すでにDAOレコードセットは開いていて、フォームと連結しています。
そのレコードセットは、Me.Recordset または Me.RecordsetClone でアクセスできます。
ゆえに、やはり、変数宣言は不要です。
レコードセットは宣言してそれに代入してもいいですが、DAO.Database の宣言は不要です。
hirotonさんの#1の回答のコードや、#4の私の回答のリンク先のコードを参照してください。
haenaさん ありがとうございます。そういう()だったのですね。こういう事も認識ないと単純に()なしで記述してしまうので勉強になりました。
別件のhirotonさんからの「処理させていた」のなら「出来ている」のでは?に回答出来ていませんでした。(遅くなりました)
元々更新クエリを数ヶ作成してフォームのフィルターに合わせて実行させていたのです。条件1のときはクエリ1、条件2の時はクエリ2の形で(If分で分岐)。今回色々フォーム変更が必要になり抽出条件が異なるだけの複数のクエリを1つにまとめたいのが目的でした。それを出来るならクエリ作成せずに出来ればと思ったのです。
hatenaさんの最後の回答を見る前に投稿してしまいました。失礼しました。
この場合DAOとの接続宣言は不要なのですね。因みにDim db As DAO.DatabaseやDim rs As DAO.Recordsetの宣言はどういう時に必要なのでしょうか? ネットでのコード見るとこれがあるので、毎回必要と思ってました。
初歩的な事ばかりですみません。
DBEngine.OpenDatabase メソッドまたは Workspace.OpenDatabase メソッドを使用する場合。
DAO.TableDef オブジェクト型の変数を使用して任意のテーブル定義を参照する場合。
DAO.Recordset オブジェクトの Filter プロパティや Sort プロパティを使用する場合。
Access とは別の Office アプリケーション( Excel や Word などの)の VBA プロジェクトにおいて DAO を使用する場合。
あとこれも。
まず、AccessのUIでのデータベース操作はDAOを使っています。
クエリ、フォーム、レポートなど、DoCmd系のデータベース操作など。
まずは、これを理解しておいてください。
そこで、
ネットでよくあるコードの一例
これは現在開いてコードを記述しいるAccessファイル以外のAccessファイルを操作するコードになります。具体的には捜査対象は C:\daosample\sample.accdbファイルです。
現在開いてコードを記述しいるAccessファイル内のテーブルを操作する場合は、
Accessファイルを開いた時点で、DAOデータベースは自動で開かれています。よって、OpenDatabaseの記述は不要です。現在開かれているデータベースはCurrentDBで取得できます。
これは変数宣言を省略して下記のように記述してもOKです。
次に、フォームのデータ操作する場合、フォームを開いた時点で、DAOレコードセットも自動で開かれます。フォームのUIでこのレコードセットを操作している仕組みになってます。
ですので、フォームのデータをVBAで操作する場合、OpenRecordsetでわざわざ開く必要はありません。
フォームのレコードセットにアクセスする方法は、Me.Recordset、Me.RecordsetClone、Me.Recordset.Clone があります。それらの違いについては、#4の回答のリンク先で解説しています。
Withステートメントを使用すれば変数宣言も不要になります。
これは、OpenRecordsetの場合も同様に省略することはできますが、
あまり見かけませんね。
こんな感じで理解できたでしょうか。
hatenaさん何度も回答ありがとうございます。
大変分かり易い説明をお手数かけました。そういう事だったのですね。納得しました。
色々な情報がごっちゃになっており、混乱してました。
本当にありがとうございました。
オブジェクト変数の作成
変数宣言が必要なのは同一のオブジェクトを操作する必要があるとき、ですかね
間違った実装
hatenaさんも解説されていますが、
With
ステートメントを使うとこんな時でも変数宣言をしなくてもよくなります複雑になってくると、どのオブジェクトをWithステートメントで省略したらいいか悩みどころで、省略しないほうが見やすいコードという場合もあります
コードが長くなってくると、
//なにかする
のところでWith
で省略した記述がrsMe
なのか、rsA
なのか・・・と本人でも混乱するようになってくるので見やすさ重視で変数を使うなんてこともよくありますApplication.CurrentDb メソッド (Access)
Access の CurrentDB はキャッシュしよう(IT raises ITさん)
CurrentDb
についても、本来はとしたり、または
With
ステートメントで参照して使用したほうが正しい使い方ができます。上記のような、クエリを複数回実行するのような場合を考えると、実は「CurrentDb.Execute
」を繰り返すとは少し違う動作をしますまぁ、よほどのことがない限り、ただクエリを実行するだけであれば
CurrentDb.Execute
の1行だけで済ませられるメリットを優先で問題ないので、それで回答している感じですねhirotonさん・skさんもありがとうございます。実際使って試しなががら勉強していく様にします。それにしても奥が深いものですね。でも上手く出来た時は感激しますので頑張っていきます。
色々とありがとうございました。