毎回お世話になってます。
非連結フォームの値をテーブルに追加したいのですが、CurrentDb.Execute strSQLでは”実行時エラー'3061'
パラメータが少なすぎます。4を指定して下さい。”となります。CurrentDb部を DoCmd.RunSQL strSQL にすると正常に動作します。理由が分からないのです(私があまり理解してないからだと思ってますが)。もちろん参照設定はDAOにしてます。
実行したいSQLは下記になります。CurrentDb.Executeでは出来ない場合があるのでしょうか?
Dim strSQL As String
strSQL = _
"INSERT INTO [T_登録中継用_改修履歴] " & _
"([製造履歴ID], [受付日付], [改修内容], [完了日付], [MEMO]) " & _
"SELECT " & _
"[T_製造履歴].[製造履歴ID], " & _
" Forms![F_改修履歴に追加設定]![tx受付日付代入用], " & _
" Forms![F_改修履歴に追加設定]![tx改修内容代入用], " & _
" Forms![F_改修履歴に追加設定]![tx完了日付代入用], " & _
" Forms![F_改修履歴に追加設定]![tx_memo代入用] " & _
"FROM [T_製造履歴] " & _
"WHERE [T_製造履歴].[check]=True;"
```
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つ不足しています」とかなるとわかりやすいんじゃないかと
変数 strSQL に格納されている SQL がパラメータクエリになっているからです。
以上の 4 箇所がそれぞれ暗黙的なパラメータとして認識されています。
そして DAO.Database オブジェクトの Execute メソッドでは、
パラメータへの値渡しを行うことが出来ません。
この場合は、次のいずれかの方法を用いるのが妥当です。
後者の方法は、主に以下のようなケースにおいて用いるのが適しています。
特定のインターフェース(この場合はフォーム)に依存せず、パラメータに任意の値を直接渡したい場合
実行されたクエリ/SQLによって影響を受けたレコードの件数を取得したい場合
DAO.Workspace オブジェクトと連携してトランザクション処理を行いたい場合
なお、任意のコントロールの値を「 SQL 内におけるリテラル」として文字列連結させる手法もよく見られますが、以下のようなケースに備えてエスケープ処理を行うことが望ましいでしょう。
コントロールの値(文字列)にシングルクォーテーション( ' )やダブルクォーテーション(")、改行(などの制御文字)が含まれている場合。
コントロールの値が Null である場合。
DAO.QueryDef オブジェクトを使用してパラメータの値渡しを直接行なうのであれば、エスケープ処理は不要です。
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;"
変数 strSQL に格納された SQL 文の中に「パラメータと解釈し得る要素」が含まれておらず、
また SQL 全体において構文上の不備がないからです。
この場合の「パラメータと解釈し得る要素」とは、主に次のようなものです。
データベース上のどのテーブル/クエリの名前とも一致しない識別子
クエリから呼び出されたテーブル/クエリのどのフィールドの名前とも一致しない識別子
件の INSERT INTO 文 においては、
Forms![F_改修履歴に追加設定]![tx受付日付代入用]などの記述はFROM 句で呼び出されている[T_製造履歴]のどのフィールドの名前とも一致しないため、
暗黙的に「そういう名前のパラメータである」と解釈されます。
上記の場合は非連結テキストボックス[change_Date]の値が
「 SQL 内における日時リテラル」として組み込まれるよう
文字列の整形と文字列連結が行われており、最終的に
UPDATE 文の中に「パラメータと解釈し得る要素」が
含まれないようになっています。
ちなみに、この時の非連結テキストボックス[change_Biko]の値が
「I'm sorry.」という文字列だった場合、SQL 上の構文エラーが発生します。
文字列リテラルの囲み記号としてシングルクォーテーションを使用する場合は、
文字としてのシングルクォーテーションを次のようにエスケープした方がよいでしょう。
SQL 文のチェック作業を行なう際には、
以上のようにしておき、生成された SQL 文が実際に
どのようになっているかを確認してみることをお奨めします。
skさん ありがとうございます。フィールドの名前とも一致しない識別子の事も大変重要ですね。今まで認識してなかったので勉強になりました。ACCESSは名前で判断されるので注意が必要ですね。
確かに「I'm sorry.」ではエラー出ましたのでReplace・・・にしたらOKでした。
細かい設定をしないと、どこでエラーになるか分かりませんね、いい経験となりました。ACCESSは奥が深く大変ですが、コツコツするしかありません。色々な事を教えて頂けて大変有り難いです。
本当に感謝です。