Microsoft Access 掲示板

VBAでSQL更新

16 コメント
views
4 フォロー

SQLを勉強中の者です。ファーム(S_1)値を使いテーブル(T1)の更新するのに下記のコードで実行したところSQLエラーとなります。どこが間違ってますでしょうか? 多分クオーテーションだと思うのですが・・・
strSQL1 = "UPDATE [T_1] " & _
                 "SET [F1]=[Forms]![S_1]![c1], " & _
                     "[F2]=[Forms]![S_1]![c2], " & _
                     "[F3]=[Forms]![S_1]![c3], " & _
                     "[F4]=[Forms]![S_1]![c4], " & _
                  "WHERE [ID]=" & [Forms]![S_1]![txID] & " ;"

あと [Forms]![S_1]![c1~C4]を変数にした場合、どうつないだらいいでしょうか?

一郎
作成: 2024/06/26 (水) 15:42:06
通報 ...
1
  • テーブル[T_1]のそれぞれのフィールドのデータ型は何なのか。

  • その SQL 文をどのオブジェクトのどのメソッドによって実行しようとしているのか。

とりあえず以上の 2 点について明記されることをお奨めします。

2
ビギナー 2024/06/26 (水) 16:47:37 ddfe5@b96a9

SK様 T_1のF1は数値型、F2は通貨型、F3は日付型、F4はテキスト型です。
T_1テーブルの上記フィールドを非連結フォームに入力値に更新したいのです。T_1の入力済み値をS_1フォームから変更したいのです。 説明不足でした。

3

その SQL 文をどのオブジェクトのどのメソッドによって実行しようとしているのか。

以上の点についてもお答え下さい。

"[F4]=[Forms]![S_1]![c4], " & _

明らかに誤っているのは「最後の代入式の直後に ","(カンマ) が挿入されている」という点ですが、もし DoCmd オブジェクトの RunSQL メソッドではなく DAO.Database オブジェクト(または ADODB.Connection オブジェクト)の Execute メソッドによって SQL を実行されようとしているのであれば、それとはまた別の理由によって実行に失敗するでしょう。

4
ビギナー 2024/06/27 (木) 08:56:31 ddfe5@b96a9

SK様 実行はDoCmd.RunSQL strSQL1 でしてます。明らかに間違ってのカンマを削除したら更新できました(初歩的なミスでした。) すみませんでした。あと SQL部の[Forms]![S_1]![c1~C4]を変数にした場合はどう記述するといいでしょうか? これが上手く出来なくて

7

T_1テーブルの上記フィールドを非連結フォームに入力値に更新したい

SQL部の[Forms]![S_1]![c1~C4]を変数にした場合はどう記述するといいでしょうか?

各非連結コントロールの値を「 SQL 文におけるリテラル」として組み込みたい、
という意味でおっしゃっているのであれば、その SQL の実行時において、
いずれかの非連結コントロールの値が Null であることを許容するか否か次第です。

単純に Null 値をそのまま文字列連結させてしまうと、実行時に構文エラーが
発生する可能性が極めて高いでしょう。

8
'非連結フォーム[S_1]のフォームモジュール
Option Compare Database
Option Explicit

Private Const SourceTableName As String = "T_1"

'コマンドボタン[cmdUpdate]の[クリック時]イベント
Private Sub cmdUpdate_Click()
On Error GoTo Err_cmdUpdate_Click

    '入力チェック処理を実行し、その結果が False だった場合
    If InputCheck() = False Then
        'このプロシージャを抜ける
        Exit Sub
    End If

    Dim lngResult As VbMsgBoxResult

    '実行確認ダイアログを表示し、どのボタンがクリックされたかを受け取る
    lngResult = MsgBox("レコードの更新を実行しますか?", _
                        vbQuestion + vbYesNo + vbDefaultButton2, _
                        "実行確認")

    '[いいえ]ボタンがクリックされた場合
    If lngResult = vbNo Then
        'このプロシージャを抜ける
        Exit Sub
    End If

    Dim strSQL As String

    'UPDATE 文の生成処理を実行し、その結果を変数 strSQL に渡す
    strSQL = CreateUpdateStatement()

    '生成に失敗した場合
    If strSQL = "" Then
        'このプロシージャを抜ける
        Exit Sub
    End If

    Debug.Print strSQL

    DoCmd.SetWarnings False
    'SQL 文の実行
    DoCmd.RunSQL strSQL
    DoCmd.SetWarnings True

    MsgBox "レコードが更新されました。", _
           vbInformation, _
           "実行完了"

'終了処理
Exit_cmdUpdate_Click:
On Error Resume Next

    DoCmd.SetWarnings True

    Exit Sub

'エラー時処理
Err_cmdUpdate_Click:

    Dim strErrTitle As String
    Dim strErrMsg As String

    strErrTitle = "実行時エラー (" & Me.Name & ".cmdUpdate_Click)"
    strErrMsg = Err.Number & ": " & Err.Description

    Debug.Print strErrTitle
    Debug.Print strErrMsg

    MsgBox strErrMsg, vbCritical, strErrTitle

    Resume Exit_cmdUpdate_Click
End Sub
9
'各コントロールの入力状態をチェックする関数。
'全てのコントロールの値が適切であれば True、そうでなければ False を返す
Private Function InputCheck() As Boolean
On Error GoTo Err_InputCheck

    InputCheck = False

    '[txtID]のチェック(オートナンバー型の主キー[ID]と照応)
    With Me![txtID]
        
        If IsNull(.Value) = True Then
            MsgBox "[ID]が入力されていません。", _
                   vbExclamation, _
                   "入力エラー"
            .SetFocus
            Exit Function
        End If
        
        If IsNumeric(.Value) = False Then
            MsgBox "数値データに変換できない文字列が[ID]に入力されています。", _
                   vbExclamation, _
                   "入力エラー"
            .SetFocus
            Exit Function
        End If

        If DCount("*", SourceTableName, "[ID]=" & CLng(.Value)) = 0 Then
            MsgBox "テーブル[" & SourceTableName & "]上に、[ID]の値が" & CLng(.Value) & "であるレコードは存在しません。", _
                   vbExclamation, _
                   "入力エラー"
            .SetFocus
            Exit Function
        End If

    End With

    '[c1]のチェック(数値型フィールド[F1]と照応)
    With Me![c1]
        
        If Nz(.Value, "") <> "" Then
            If IsNumeric(.Value) = False Then
                MsgBox "数値データに変換できない文字列が[F1]に入力されています。", _
                       vbExclamation, _
                       "入力エラー"
                .SetFocus
                Exit Function
            End If
        End If

    End With

    '[c2]のチェック(通貨型フィールド[F2]と照応)
    With Me![c2]
        
        If Nz(.Value, "") <> "" Then
            If IsNumeric(.Value) = False Then
                MsgBox "数値データに変換できない文字列が[F2]に入力されています。", _
                       vbExclamation, _
                       "入力エラー"
                .SetFocus
                Exit Function
            End If
        End If

    End With

    '[c3]のチェック(日付/時刻型フィールド[F3]と照応)
    With Me![c3]
        
        If Nz(.Value, "") <> "" Then
            If IsDate(.Value) = False Then
                MsgBox "日時データに変換できない値が[F3]に入力されています。", _
                       vbExclamation, _
                       "入力エラー"
                .SetFocus
                Exit Function
            End If
        End If

    End With

    InputCheck = True

'終了処理
Exit_InputCheck:

    Exit Function

'エラー時処理
Err_InputCheck:

    InputCheck = False

    Dim strErrTitle As String
    Dim strErrMsg As String

    strErrTitle = "実行時エラー (" & Me.Name & ".InputCheck)"
    strErrMsg = Err.Number & ": " & Err.Description

    Debug.Print strErrTitle
    Debug.Print strErrMsg

    MsgBox strErrMsg, vbCritical, strErrTitle

End Function
10
'各コントロールの値に応じた UPDATE 文を生成する関数
Private Function CreateUpdateStatement() As String
On Error GoTo Err_CreateUpdateStatement

    CreateUpdateStatement = ""

    Dim strUpdateClause As String
    Dim strSetClause As String
    Dim strWhereClause As String

    With Me
        
        'UPDATE 句の設定
        strUpdateClause = "UPDATE [T_1] "
        
        'ここから SET 句の設定
        strSetClause = ""
        
        '[F1]に対する代入式の設定
        If IsNumeric(![c1].Value) = True Then
            strSetClause = strSetClause & ", " & _
                           "[F1]=" & CDec(![c1].Value)
        Else
            strSetClause = strSetClause & ", " & _
                           "[F1]=Null"
        End If
        
        '[F2]に対する代入式の設定
        If IsNumeric(![c2].Value) = True Then
            strSetClause = strSetClause & ", " & _
                           "[F2]=" & CCur(![c2].Value)
        Else
            strSetClause = ", " & _
                           "[F2]=Null"
        End If
        
        '[F3]に対する代入式の設定
        If IsDate(![c3].Value) = True Then
            strSetClause = strSetClause & ", " & _
                           "[F3]=#" & Format(![c3].Value, "yyyy/mm/dd hh:nn:ss") & "#"
        Else
            strSetClause = strSetClause & ", " & _
                           "[F3]=Null"
        End If
        
        '[F4]に対する代入式の設定
        If Nz(![c4].Value, "") <> "" Then
            strSetClause = strSetClause & ", " & _
                           "[F4]='" & Replace(![c4].Value, "'", "''", 1, -1, vbBinaryCompare) & "'"
        Else
            strSetClause = strSetClause & ", " & _
                           "[F4]=Null"
        End If
        
        strSetClause = "SET " & Mid(strSetClause, 2) & " "
        
        'ここまで SET 句の設定
        
        'ここから WHERE 句の設定
        
        If IsNumeric(![txtID].Value) = True Then
            strWhereClause = "WHERE [ID]=" & CLng(![txtID].Value)
        Else
            '[ID]の指定が正しくなければ戻り値を空文字のまま終了
            Exit Function
        End If
        
        'ここまで WHERE 句の設定

    End With
        
    'UPDATE 句、SET 句、WHERE 句を連結して 1 つの SQL 文とした結果を戻り値として返す
    CreateUpdateStatement = strUpdateClause & vbCrLf & _
                            strSetClause & vbCrLf & _
                            strWhereClause & ";"

'終了処理
Exit_CreateUpdateStatement:

    Exit Function

'エラー時処理
Err_CreateUpdateStatement:

    Dim strErrTitle As String
    Dim strErrMsg As String

    strErrTitle = "実行時エラー (" & Me.Name & ".CreateUpdateStatement)"
    strErrMsg = Err.Number & ": " & Err.Description

    Debug.Print strErrTitle
    Debug.Print strErrMsg

    MsgBox strErrMsg, vbCritical, strErrTitle

    CreateUpdateStatement = ""

End Function
12

1箇所修正。

strUpdateClause = "UPDATE [T_1] "

        strUpdateClause = "UPDATE [" & SourceTableName & "] "
13

もう1箇所。

            strSetClause = ", " & _
                           "[F2]=Null"

            strSetClause = strSetClause & ", " & _
                           "[F2]=Null"
11

各コントロールの入力チェック、コントロールの値に応じた条件分岐、
文字列に含まれる引用符のエスケープ処理などを考慮するのであれば、
以上のサンプルのような形となるでしょう。

ただ私個人の見解としては、こういうまどろっこしい文字列連結を行うコードを
記述するより、パラメータクエリを実行するようにした方が良いと思います。

5
hatena 2024/06/27 (木) 09:54:21 修正

変数は文字列の外(""の外)に出して&で結合すればいいでしょう。
あと、SQL内では日付時刻型は#で囲む、文字列は'で囲みます。

Dim c1 As Long, c2 As Currency, c3 As Date, c4 As String
c1 = [Forms]![S_1]![c1]
c2 = [Forms]![S_1]![c2]
c3 = [Forms]![S_1]![c3]
c4 = [Forms]![S_1]![c4]
strSQL1 = "UPDATE [T_1] " & _
                 "SET [F1]=" & c1 & ", " & _
                     "[F2]=" & c2 & ", " & _
                     "[F3]=#" & Format(c3,"yyyy/mm/dd") & "#, " & _
                     "[F4]='" & c4 & "' " & _
                  "WHERE [ID]=" & [Forms]![S_1]![txID] & " ;"

日付をFormatで書式設定しているのは、OSの日付書式設定によってはうまく行かない場合があるのでその対策です。

テキストボックスが未入力だったりした場合のエラー対策は別途必要になります。

6
ビギナー 2024/06/27 (木) 11:25:35 ddfe5@b96a9

hatena様 ありがとうございます。そのコードで問題なく更新できました。VBA内での改行が絡み頭が混乱してました。
一つ教えて頂きたいのですが、SQL内でフォーム値を使う場合も変数と同じ様に&でつなぎ外に出さないといけないのですよね? 質問時の "SET [F1]=[Forms]![S_1]![c1], " & _ は本来間違っているのですね?( "SET [F1]=" & [Forms]![S_1]![c1] & ", " & _ が正しい) たまたま動作していたのですね? 

14
ビギナー 2024/06/27 (木) 12:23:22 ddfe5@b96a9

SK様 失礼しました。hatena様の分の前に詳しい回答を沢山頂いていたのを見落としてました。申し分けありません。
Null値の事は気になってました。説明不足でしたがフォームAのレコードをダブルクリックで変更用のフォームS1が開き、S1で変更しSQLで更新の仕組みにしてます。S1のNULL対策として元々のフォームA値をS1の別のテキストボックスに代入してNULLのテキストボックスにはそれを代入してから更新としてます。

15

hatena様の分の前に詳しい回答を沢山頂いていたのを見落としてました。

それぞれの投稿の投稿者アイコンの下にある番号が、このスレッドにおける投稿順を示しています。

私が回答したのは hatena さん( 5 番)よりも後です。
ビギナーさんの投稿( 4 番)に返信する形を取ったため、その階層ツリーのノードとして表示されているだけです。

フォームAのレコードをダブルクリックで変更用のフォームS1が開き

[フォームA]がテーブル[T_1]をレコードソースとする帳票フォームであるとして、

元々のフォームA値をS1の別のテキストボックスに代入して
NULLのテキストボックスにはそれを代入

フォーム[S_1]の各非連結コントロールへの値の代入に際し、[フォームA]のカレントレコードから参照されたいずれかのフィールドの値が元から Null であるケースはないのでしょうか。

S1で変更しSQLで更新の仕組みにしてます。

少なくとも、フォーム[S_1]のいずれかの非連結コントロールの値がユーザーの操作によって Null にされる可能性はゼロではないはずです。

16
ビギナー 2024/06/27 (木) 15:09:49 ddfe5@b96a9

sk様 回答の表示はそういう事だったのですね。
変更用フォーム[S_1]の非連結コントロールは編集可能なのでユーザーでNullにされる可能性はありますので、変更実行ボタン(それに更新SQLコードを記載)に実行前にNull値には別のコントロール値(元から代入させていた)を代入する方法をとってます。