Dim strSQL As String
If Not IsNull(Me!change_Date) Then strSQL = ", [完成日付]=" & Format(Me!change_Date, "#yyyy/mm/dd#")
If IsDate(Me!change_Kokyaku) Then strSQL = strSQL & ", [顧客ID]=" & Me!change_Kokyaku
If Not IsNull(Me!change_Firm) Then strSQL = strSQL & ", [firmID]=" & Me!change_Firm
If Not IsNull(Me!change_Tanto) Then strSQL = strSQL & ", [製造担当者ID]=" & Me!change_Tanto
If Not IsNull(Me!change_Biko) Then strSQL = strSQL & ", [備考]='" & Me!change_Biko & "'"
If strSQL = "" Then Exit Sub
strSQL = "UPDATE [T_製造履歴] SET " & Mid(strSQL, 3) & " WHERE [check]=True;"
'フォーム[F_改修履歴に追加設定]のフォームモジュールから実行する場合
Dim dbTarget As DAO.Database
Dim qdfTemp As DAO.QueryDef
Dim lngInsertedCount As Long
'カレントデータベースの参照
Set dbTarget = CurrentDb
'一時的なクエリを作成する
Set qdfTemp = dbTarget.CreateQueryDef("", strSQL)
With qdfTemp
'各パラメータに値を渡す
.Parameters("Forms![F_改修履歴に追加設定]![tx受付日付代入用]").Value = Me![tx受付日付代入用].Value
.Parameters("Forms![F_改修履歴に追加設定]![tx改修内容代入用]").Value = Me![tx改修内容代入用].Value
.Parameters("Forms![F_改修履歴に追加設定]![tx完了日付代入用]").Value = Me![tx完了日付代入用].Value
.Parameters("Forms![F_改修履歴に追加設定]![tx_memo代入用]").Value = Me![tx_memo代入用].Value
'クエリを実行する
.Execute dbFailOnError
'クエリによって影響を受けたレコードの件数を取得する
lngInsertedCount = qdfTemp.RecordsAffected
End With
Set qdfTemp = Nothing
Set dbTarget = Nothing
If lngInsertedCount > 0 Then
MsgBox "[T_登録中継用_改修履歴]に " & lngInsertedCount & " 件のレコードが追加されました。", _
vbInformation, _
"実行完了"
Else
MsgBox "追加対象となるレコードがありません。", _
vbInformation, _
"対象レコードなし"
End If
Dim strSQL As String
If Not IsNull(Me!change_Date) Then strSQL = ", [完成日付]=" & Format(Me!change_Date, "\#yyyy/mm/dd\#")
If IsDate(Me!change_Kokyaku) Then strSQL = strSQL & ", [顧客ID]=" & Me!change_Kokyaku
If Not IsNull(Me!change_Firm) Then strSQL = strSQL & ", [firmID]=" & Me!change_Firm
If Not IsNull(Me!change_Tanto) Then strSQL = strSQL & ", [製造担当者ID]=" & Me!change_Tanto
If Not IsNull(Me!change_Biko) Then strSQL = strSQL & ", [備考]='" & Me!change_Biko & "'"
If strSQL = "" Then Exit Sub
strSQL = "UPDATE [T_製造履歴] SET " & Mid(strSQL, 3) & " WHERE [check]=True;"
CurrentDb.Execute strSQL
If IsNull(Me!change_Date) And IsNull(Me!change_Kokyaku) And IsNull(Me!change_Firm) And IsNull(Me!change_Tanto) And IsNull(Me!change_Biko) Then Exit Sub
Dim strSQL As String
strSQL = "UPDATE [T_製造履歴] SET "
If Not IsNull(Me!change_Date) Then strSQL = strSQL & "[完成日付]=#" & Format(Me!change_Date, "yyyy/mm/dd") & "#, "
If Not IsNull(Me!change_Kokyaku) Then strSQL = strSQL & "[顧客ID]=" & Me!change_Kokyaku & ", "
If Not IsNull(Me!change_Firm) Then strSQL = strSQL & "[firmID]=" & Me!change_Firm & ", "
If Not IsNull(Me!change_Tanto) Then strSQL = strSQL & "[製造担当者ID]=" & Me!change_Tanto & ", "
If Not IsNull(Me!change_Biko) Then strSQL = strSQL & "[備考]='" & Me!change_Biko & "' "
strSQL = strSQL & "WHERE [check]=-1 ;"
CurrentDb.Execute strSQL
hatena様のAllopenForms()を実行したところ、エラーなく実行できました。
原因はprpを利用していないからだと思います。
以下の通り、For Each prp In frm.Properties から以下3行を追加し、実行したところ、
For Each prp In frm.Properties にて「実行時エラー’13’ 型が一致しません。」が表示されました。
prpの使い方が分かっておらずfrmのように扱うことができないのでしょうか。
Sub AllOpenForms()
Dim frm As Form, prp As Property
' Enumerate Forms collection.
For Each frm In Forms
' Print name of form.
Debug.Print frm.Name
Sub AllOpenForms()
Dim frm As Form, prp As Property
' Enumerate Forms collection.
For Each frm In Forms
' Print name of form.
Debug.Print frm.Name
Debug.Print frm.Properties.Count
Debug.Print frm.Properties(0).Name
Debug.Print frm.Properties(0).Value
Next frm
End Sub
skさん ありがとうございます。フィールドの名前とも一致しない識別子の事も大変重要ですね。今まで認識してなかったので勉強になりました。ACCESSは名前で判断されるので注意が必要ですね。
確かに「I'm sorry.」ではエラー出ましたのでReplace・・・にしたらOKでした。
細かい設定をしないと、どこでエラーになるか分かりませんね、いい経験となりました。ACCESSは奥が深く大変ですが、コツコツするしかありません。色々な事を教えて頂けて大変有り難いです。
本当に感謝です。
変数 strSQL に格納された SQL 文の中に「パラメータと解釈し得る要素」が含まれておらず、
また SQL 全体において構文上の不備がないからです。
この場合の「パラメータと解釈し得る要素」とは、主に次のようなものです。
データベース上のどのテーブル/クエリの名前とも一致しない識別子
クエリから呼び出されたテーブル/クエリのどのフィールドの名前とも一致しない識別子
件の INSERT INTO 文 においては、
Forms![F_改修履歴に追加設定]![tx受付日付代入用]などの記述はFROM 句で呼び出されている[T_製造履歴]のどのフィールドの名前とも一致しないため、
暗黙的に「そういう名前のパラメータである」と解釈されます。
上記の場合は非連結テキストボックス[change_Date]の値が
「 SQL 内における日時リテラル」として組み込まれるよう
文字列の整形と文字列連結が行われており、最終的に
UPDATE 文の中に「パラメータと解釈し得る要素」が
含まれないようになっています。
ちなみに、この時の非連結テキストボックス[change_Biko]の値が
「I'm sorry.」という文字列だった場合、SQL 上の構文エラーが発生します。
文字列リテラルの囲み記号としてシングルクォーテーションを使用する場合は、
文字としてのシングルクォーテーションを次のようにエスケープした方がよいでしょう。
SQL 文のチェック作業を行なう際には、
以上のようにしておき、生成された SQL 文が実際に
どのようになっているかを確認してみることをお奨めします。
hirotonさん skさん 詳細な回答ありがとうございました。なりほどそういう事だったのですね。
因みに前回質問分でhirotonさんとhatenaさんから回答頂いた下記はCurrentDb.Execute strSQLでいけたのですが、これは何故でしょうか? 更新と追加の違いはありますが(完全に理解出来てなくすみません)
Dim strSQL As String
If Not IsNull(Me!change_Date) Then strSQL = ", [完成日付]=" & Format(Me!change_Date, "#yyyy/mm/dd#")
If IsDate(Me!change_Kokyaku) Then strSQL = strSQL & ", [顧客ID]=" & Me!change_Kokyaku
If Not IsNull(Me!change_Firm) Then strSQL = strSQL & ", [firmID]=" & Me!change_Firm
If Not IsNull(Me!change_Tanto) Then strSQL = strSQL & ", [製造担当者ID]=" & Me!change_Tanto
If Not IsNull(Me!change_Biko) Then strSQL = strSQL & ", [備考]='" & Me!change_Biko & "'"
If strSQL = "" Then Exit Sub
strSQL = "UPDATE [T_製造履歴] SET " & Mid(strSQL, 3) & " WHERE [check]=True;"
なお、任意のコントロールの値を「 SQL 内におけるリテラル」として文字列連結させる手法もよく見られますが、以下のようなケースに備えてエスケープ処理を行うことが望ましいでしょう。
コントロールの値(文字列)にシングルクォーテーション( ' )やダブルクォーテーション(")、改行(などの制御文字)が含まれている場合。
コントロールの値が Null である場合。
DAO.QueryDef オブジェクトを使用してパラメータの値渡しを直接行なうのであれば、エスケープ処理は不要です。
変数 strSQL に格納されている SQL がパラメータクエリになっているからです。
以上の 4 箇所がそれぞれ暗黙的なパラメータとして認識されています。
そして DAO.Database オブジェクトの Execute メソッドでは、
パラメータへの値渡しを行うことが出来ません。
この場合は、次のいずれかの方法を用いるのが妥当です。
後者の方法は、主に以下のようなケースにおいて用いるのが適しています。
特定のインターフェース(この場合はフォーム)に依存せず、パラメータに任意の値を直接渡したい場合
実行されたクエリ/SQLによって影響を受けたレコードの件数を取得したい場合
DAO.Workspace オブジェクトと連携してトランザクション処理を行いたい場合
DoCmdは「ACCESS」の機能を呼び出します「ACCESS」は、自身がどんなフォームがあるだとか、そこにどんなコントロールがあるだとか認識できます。なので、クエリ実行時(DoCmd.RunSQL)に
Forms![F_改修履歴に追加設定]![tx受付日付代入用]が何者か判断できますCurrentDb.Executeは「DAO」の機能を呼び出します「DAO」はACCESSの構文を解釈する機能はないので、
Forms![F_改修履歴に追加設定]![tx受付日付代入用]を一つの、そのような名称のパラメータ(変数)として解釈しようとします。結果、そのようなパラメータを用意していないDAOを実行しようとして、パラメータが少なすぎますとエラーを返してきていますCurrentDb.Executeを使うのであれば、DAOで実行する前に、VBAで値の変換を済ませておく(strSQLの中にフィールド指定の構文を入れない)必要があります参考
パラメータクエリを含むSQLをVBAから実行する(hatena chipsさん)
英語でのエラーだと、たとえば
のようになるようです。ネイティブならすんなり意味が分かるんでしょうか。日本語にするなら
隠れた文字が表現されているといいと思いますし、意訳して「パラメータが1つ不足しています」とかなるとわかりやすいんじゃないかと
hirotonさん ありがとうございます。大変勉強になります。
""では上手く検出出来ませんでしたが、よく考えると全角スペースには有効ではありませんでした。この非連結テキストボックスには入力し易い様にプロパティに ひらがな 設定してます。なので全角状態になっているからでした。なのでフォーカス喪失後イベントに下記を記述しました。他の変更用非連結部は全てコンボボックスからの選択ですので大丈夫です。
確かに「備考を削除する」の想定もありますね。様子みて作成していきます。
Nullと""、" " の違いは難しいですね。本当にありがとうございました。
通常、非連結のテキストボックスに
""(長さ0の文字列)が入ることはありませんフォーム上での人の操作で「何か入力」→「Delete等で削除」を行うと、非連結のテキストボックスの内容は
NULLになります。(レコードソースに連結されたコントロールの場合、設定によっては""が入る場合があります)VBAによる操作等で、直接
Me!テキストボックス = ""のような処理を行うと、非連結のテキストボックスでもその値が""になったりはしますそもそも気にしていなかったのですが、逆に、「備考を削除したい」という場合はないのでしょうか?
「NULLなので更新対象外」と、「内容削除のために
""が入力されている」は実作業の上では判別できないので、「備考を削除する」チェックボックスを追加で用意するとかになると思いますまた、とりあえず、回答では
IsNull()を使っていますが、NULLも""同じように扱われればよいならMe!テキストボックス & "" = ""で判定できますそのほかのコントロールについても、データ型が文字列型以外になるのならば
IsDate()やIsNumeric()等で判定してあげるとより良いと思いますhirotonさん hatenaさん ありがとうございます。
なるほどです! 複数FilterのWHERE組立の様にしたらいいのですね(,の件も確かにそうですね)。
コマンドボタンにコード記述したら上手くいきました。
あと一つだけアイデア頂きたいのですがchange_Bikoに誤って""(長さ0の文字列)だけが入力されて、それが更新に反映されるのを防ぎたいのですが、それはどうすればいいでしょうか?
★この履歴の更新ですが登録ミスの変更や仕様変更等が発生した場合の変更をまとめてする為のものです(最終的な正しい履歴にする為の)。
宜しくお願いします。
あー、そうですね。訂正ありがとうございます
フォームに見えてるデータならDAOでやるかなぁとか考えていたらろくにテストもせず投稿してしまいました
前者のコードだと、Me!change_Biko がNullのとき、
WHEREの直前に,が来ることになりSQLエラーになりますね。例えば、直前に
,を付けておいて最後に先頭の,を削除してSQL文を完成させるようにすればいいでしょう。後者のコードはカレントレコードの値で上書きしてしまいますね。
「元の値と同じ値で書き換える」なら下記のような感じになるかと。
履歴って更新するものなの?
プログラミングにおける変数への値の代入処理は右辺に自分自身(変数)を指定して、自身の内容を書き換える処理が記述できます
ここで記述している「
=」は変数への代入命令の記述であるということに注意してください入力状態をチェックしながら
strSQLを生成していけばいいですねもしくは、「元の値と同じ値で書き換える」としても更新分だけを更新することになります
Nz関数を使って、「更新後の値がなければ元の値を使う」ように指示しています
hatenaさん ありがとうございます。そういった事はあまり情報ありませんので知識乏しい私には大変有り難いです。随時変更していきたいと思います。今回もお世話になりました。
テーブル削除→テーブル作成と、データ削除→データ追加では、やはり前者の方が負荷が高いですよね。実際に比較テストしたわけではないので感覚的なものですが。
また、テーブル作成クエリでは細かいプロパティ設定はできないし、リレーションシップを設定してあるとそれを削除してからでないとテーブル削除できないので面倒なことになります。私は運用時にテーブル削除はしない設計にします。
クロス集計クエリの列見出しとして使うフィールドの[集計]行が[グループ化]に設定されていないからでは?
hatenaさん ありがとうございます。
全く思いつかないアイデアです。これはシンプルでいいですね。SELECT Forms!の様にWHERE以外にフォーム値をもってこれるとは知りませんでした。
「テーブル削除→テーブル作成はファイル破損の危険性が高いように思いますので」⇒そういうものとは知りませんでした。
今回の連番とは別ですがSELECT INTOを幾つか使っていますが、この処理は頻繁に使う場面ではしない方がいいのですね?
削除クエリで全レコードを一旦削除して追加クエリで新たに追加した方が安全なのですね?
テーブル作成クエリ(SELECT INTO)ですか。
それより、T1テーブルは設計時に作成しておいて、削除クエリでデータ削除、追加クエリ(INSERT INTO)でデータ追加の方がいいかと思います。
テーブル削除→テーブル作成はファイル破損の危険性が高いように思いますので。
削除クエリは簡単にできると思いますので、追加クエリ(INSERT INTO)の作成法を回答します。
まずは、0から予想される追加最大値までの連番のテーブルを作成しておきます。
T_連番
下記のようなSQLのクエリを作成します。
この追加クエリを実行すればご希望の結果になります。
毎回申し訳ありません。記載ミスでした
hatena様ありがとうございます。とんでもないちょんぼをしました。orと書くくべきところを
andにしていました。
下記の部分を修正してください(赤字を緑字に)。
解決しました。
原因は参照設定だったのですね。
ありがとうございました。
環境の問題とは思っておりましたが、具体的にご説明頂き納得しました。
今後もよろしくお願いいたします。
なるほど、そういうことか。
参照設定を前者の画像のようにしたら私の環境でも再現しました。
VBA Extensibility を ADE の上におくと、VBIDEのPropertyを参照してしまうということですね。
現時点での見立てでは、恐らくこういうことなのではないかと。
では今度は、変数 prp を次のように宣言した上で
AllOpenForms プロシージャを実行してみたら
どのような結果になるかを確認してください。
当方の環境ではエラーなく実行できるし、同様の報告を今まで見たことがないので、mkさんの環境依存の問題のようです。
Object型にすることで実行できたということは、Propertyというキーワードが別のものに関連付けられている印象ですね。
新規ファイルを作成してそこで実行してみてうまく行くなら、ファイル破損が原因ということになります。
それでもエラーになるなら、Accessのアンインストール、再インストールを試してみるとかでしょうか。
それでもだめなら、Object型にすることで対処することになるでしょう。
sk様のご指摘のprpをObject型にすることで実行できました。
ありがとうございました。
propatyだと型エラーとなる原因分かりますでしょうか。
hatena様のAllopenForms()を実行したところ、エラーなく実行できました。
原因はprpを利用していないからだと思います。
以下の通り、For Each prp In frm.Properties から以下3行を追加し、実行したところ、
For Each prp In frm.Properties にて「実行時エラー’13’ 型が一致しません。」が表示されました。
prpの使い方が分かっておらずfrmのように扱うことができないのでしょうか。
Sub AllOpenForms()
Dim frm As Form, prp As Property
' Enumerate Forms collection.
For Each frm In Forms
' Print name of form.
Debug.Print frm.Name
Debug.Print frm.Properties.Count
Debug.Print frm.Properties(0).Name
Debug.Print frm.Properties(0).value
For Each prp In frm.Properties
Debug.Print prp.Name
Next prp
Next frm
End Sub
変数 prp を Object 型または Variant 型として宣言しても
同様の結果となるのでしょうか。
参考urlは私のブログですが、
そちらのコードを実行してみましたが、エラーなく説明プロパティの設定、取得ができました。
ちょっと原因が想像つかないですね。
とりあえず適当なフォームを開いておいて、下記のコードを実行するとどうなりますか。
回答ありがとうございます。
目的はバージョン管理をしたく、各オブジェクトのプロパティにバージョン情報をセットしたいです。
以下参考urlです。本来はこちらのプロパティ設定のプログラムで検証しておりましたが、
prpの部分で型エラーとなり、提示したシンプルなサンプルプログラム同様にprpの部分でエラーが発生したことから、
確認させて頂きました。
https://hatenachips.blog.fc2.com/blog-entry-466.html
どのフォームでも「実行時エラー’13’ 型が一致しません。」が表示されます。
エラー箇所は以下で、黄色く反転されます。
For Each prp In frm.Properties
以上よろしくお願いいたします。
そもそも、提示のコードは開いているフォームのプロパティを列挙するものですが、
プロパティは大量にあるので、Debug.Printでイミディエイトウィンドウに出力しても、
イミディエイトの表示上限を超えて全てを表示することはできないと思いますので、
あまり意味のないコードだと思いますが、
最終目的は何でしょうか。
提示のコードを実行してみましたが問題なく実行できました。
エラーが出たときどのコードが反転表示されてますか。
また、どのフォームを開いているときでもエラーになりますか。それとも特定のフォームの時だけですか。
ありがとうございました
解決いたしました。
hatenaさん ありがとうございます。
色々勉強します。
いつも大変お世話になってます。
いろいろ方法はありますが、
簡単かつ実なのはACCDEファイルに変換してユーザーにはACCDEファイルを配布する方法ですね。
ACCDEファイルはデータペーストして使用はできるがデザインビューやVBAは表示できないファイル形式です。
ACCDBファイルで解決して、完成したらACCDEファイルを作成してそれをユーザーに配布するという運用になります。
他にはオプションの「現在のデータベース」の設定でナビゲーションバー、メニュー、ショートカットメニューを非表示にしておく方法ですね。
これはメニューが表示されないので、メニューフォームとか独自のメニュー、ショートカットメニューを作成しておく必要があります。
デザインを修正する場合はShiftキーを押しながら開くと、オプション設定は無視して開きますので修正することができます。
hatenaさん ありがとうございます。コード自体に問題はなさそうなので安心しました(私の知識不足で間違ったままだと、よくないと思ったので)。因みに”フォームビューからデザインビューに切り替えられないように” はどうやって可能になるのでしょうか?
Form_Unloadイベント時ですね。
当方でサンプルを作成して確認したら同様のエラーがでました。
フォームビューからデザインビューに切り替えるとForm_Unloadイベントが発生しますが、通常の閉じる動作と異なる動きをするようです。なんとかならないかちょっといろいろ試してみましたが、難しいですね。
エラー処理を入れればそのエラーを無視するようにはできますが、そのためだけに余分なコードを追加するのも本末転倒のような気がします。
運用時にはフォームビューからデザインビューに切り替えられないようにしておかないとトラブルの元なるのでそのような設定にすべきですし、開発時はフォームビューから直接デザインビューに切り替えないようにすれば済む話だと思います。
hatenaさん Form_Unloadイベント時とボタンクリック時の両方に記述してます。