Microsoft Access 掲示板

SQLのWHEREのMe.filterが出来てない?

13 コメント
views

連投になりすみません。ここ最近投稿した関連です。
テーブル:T_製造履歴 を基にした一覧フォームがあり、フォーム上で抽出した分に対してチェックボックス(Yes/No)を一括でONにしたいのですがWHEREのMe.filterが効いてなく全レコードがチェックONになっるのです。フォームには抽出用のコンボボックス等が幾つかあり、それぞれ更新後にフィルターかけてまして、それは問題なく抽出出来てます。
この一括チェックONも問題なく出来てたのですが突然更新時のMe.filterが効いてなくなったのです。理由が分からなくて・・・ 更新コードは下記です。

strSQL = _
         "UPDATE [T_製造履歴] SET [check] = -1 " & _
         "WHERE (" & Me.filter & ");"
         DoCmd.SetWarnings False
         DoCmd.RunSQL strSQL
         DoCmd.SetWarnings True
         Me.Refresh
         MsgBox "更新しました"

フィルターコードは下記です

Private Sub proFilter()

      'プロシージャの作成(名前:proFilter 内容:フィルター条件作成して実行)
      
       Dim syID As Variant, nxID As Variant, seID As Variant, koID As Variant
       Dim kaYe As Variant, kaMo As Variant, kaDt As Variant, opg1 As Integer
      
       syID = Me.[cb製品種別].Value
       nxID = Me.[cbNxpIC].Value
       seID = Me.[cb製品型番].Value
       koID = Me.[cb顧客].Value
       kaYe = Me.[cb完成年].Value
       kaMo = Me.[cb完成月].Value
       kaDt = Me.[cb完成日].Value
       opg1 = Me.[opg_NumSort].Value
       
      'フィイルター条件組立
       Dim strFilter As String
    
       If Not IsNull(syID) Then
          strFilter = strFilter & " AND [検索種別ID]=" & syID
       End If
    
       If Not IsNull(nxID) Then
          strFilter = strFilter & " AND [nxpID]=" & nxID
       End If
    
       If Not IsNull(seID) Then
          strFilter = strFilter & " AND [製品ID]=" & seID
       Else
          strFilter = strFilter & " AND [製造履歴ID]=0"   '製品未選択の場合はリスト表示させない
       End If
       
       If Not IsNull(koID) Then
          strFilter = strFilter & " AND [顧客ID]=" & koID
       End If
       
       If Not IsNull(kaYe) Then
          strFilter = strFilter & " AND [完成年]=" & kaYe
       End If
       
       If Not IsNull(kaMo) Then
          strFilter = strFilter & " AND [完成月]=" & kaMo
       End If
       
       If Not IsNull(kaDt) Then
          strFilter = strFilter & " AND [完成日付]=#" & kaDt & "#"
       End If
       
       strFilter = Mid(strFilter, 6) '先頭の" AND "を削除
       Me.filter = strFilter
     
       If strFilter <> "" Then
          Me.FilterOn = True
       Else
          Me.FilterOn = False
       End If
       
      'シリアルNo.並び替え設定
       Select Case opg1
            Case 1
              Me.OrderBy = "[製品型番], [serial番号] "
            Case 2
              Me.OrderBy = "[製品型番], [serial番号] DESC"
       End Select
        
       Me.OrderByOn = True
   
End Sub
beginner
作成: 2025/04/15 (火) 16:41:38
通報 ...
1

下記のようにデバッグ用のコードを埋め込んで実行してみてください。

strSQL = _
         "UPDATE [T_製造履歴] SET [check] = -1 " & _
         "WHERE (" & Me.filter & ");"
Debug.Print strSQL 'デバッグ用
Exit Sub           'デバッグ用

イミディエイトウィンドウにstrSQLに格納されているSQL文が出力されますので、それが正しいものになっているか確認しましょう。

2
beginner 2025/04/16 (水) 09:42:41 61dd6@e1025

状況の追記です。この一覧フォームのフィルター用コンボボックスには製品ID、顧客ID、完成年、完成月、完成日があります。製品IDが未選択時はリスト表示出来ない様にしてます。各コンボ選択(更新後)ではフィルター(投稿したコードを各コンボからCall)が効いてフォームのリスト表示は問題ありません。リスト表示分のみcheckをYesに更新させるボタン(投稿したコード)で更新させる形です。その後、DoCmd.RunSQL strSQL を CurrentDb.Execute strSQL にして試しして下さいみると製品IDと顧客IDは問題なくフォーム表示分のみYesになります。完成年コンボで抽出後ではフォーム表示は問題ないのですがYesに更新実行すると”実行エラー3061 パラメータが少なすぎます。1を指定して下さい”となります(前回投稿と同様)。因みにコンボ完成月、完成日は完成年が未選択では不可にしてます。DoCmd.RunSQL strSQL では実行されるのですが完成年条件が無視されて指定の製品ID分の全レコードがcheck Yesになります。
何かコードに不備があるのでしょうか?

3

とりあえず上の回答のデバッグコードを試してみて、出力されたSQL文を提示してください。

あと、下記を確認ください。
フォームのレコードソースは「T_製造履歴」で間違いないですか。
「完成年」フィールドのデータ型は数値型ですか。

4
beginner 2025/04/16 (水) 11:05:34 61dd6@e1025

hatenaさん ありがとうございます。
試した分のイミディエイトウィンドウには UPDATE [T_製造履歴] SET [check] = -1 WHERE ([製品ID]=377 AND [完成年]=2015); と表示されました。
フォームのレコードソースは「T_製造履歴」を基にして他のテーブルを連結させたクエリとしてます。
「完成年」フィールドは日付型の完成日がT_製造履歴にあり、それを使ってYear([完成日付])をそのクエリに組み込んでます。フォームの抽出コンボ完成年のソースも同じクエリから取ってきてます。

5

「完成年」フィールドは日付型の完成日がT_製造履歴にあり、それを使ってYear([完成日付])をそのクエリに組み込んでます。

[T_製造履歴]テーブルには[完成年]フィールドはないということですよね。
ならば、[完成年]はパラメータとして判断されますので、
”実行エラー3061 パラメータが少なすぎます。1を指定して下さい”
というエラーになります。

SQLの[T_製造履歴]の部分をフォームのレコードソースのクエリに変更するか、
下記のようにサブクエリにして[完成年]、[完成月]の演算フィールドを追加すればいいでしょう。

strSQL = _
    "UPDATE " & _
    "(SELECT *, Year(完成日) As 完成年, Month(完成日) As 完成月 FROM [T_製造履歴]) " & _
    "SET [check] = -1 " & _
    "WHERE (" & Me.filter & ");"
6

ちなみに、「WHEREのMe.filterが効いてなく全レコードがチェックON」になるのは、
Accessは[完成年]をパラメータとして判断して、まずはアクティブフォームから探します。
フォームには[完成年]というフィールドがありますから、その値をパラメータ値として取得します。フォームの[完成年]を参照するとカレントレコードの値を取得します。
カレントレコードの完成年が2025年で、[cb完成年]で2025年を選択していればすべてのレコードの[check]がオンになります。

7
beginner 2025/04/16 (水) 14:31:06 61dd6@e1025

hatenaさん ありがとうございます。
UPDATE [フォームソースのクエリ名] にしたら正常に対象分(表示されているレコードリスト)のみYesに出来ました!
クエリを更新する事でソースであるテーブルを更新させてもいいのですね。
[T_製造履歴]には完成年フィールドがなかったのでフィルターが出来なかったのですね。前回投稿と同じ様な事例でした。
まだ完全には理解出来てないと思いますがパラメータになってしまうというのが大分イメージ出来ました。
自分では絶対解決出来ない事でしたので本当に助かりました。
因みにフォームの表示分を対象に色々する場面が多いのですが、SQL使わずDAOだけでも可能でしょうか? DAOは余り理解出来てないのですがフォームの表示レコードをレコードセットにするイメージで。

8

因みにフォームの表示分を対象に色々する場面が多いのですが、SQL使わずDAOだけでも可能でしょうか? 

フォームに表示されているレコードはRecordsetプロパティで取得できますので、それを対象に更新すればいいですね。

    Dim rs As DAO.Recordset
    Set rs = Me.Recordset
    rs.MoveFirst
    Do Until rs.EOF
        rs.Edit
        rs!check = -1
        rs.Update
        rs.MoveNext
    Loop
    MsgBox "更新しました"

この方法はフォームに現在表示されているレコードを対象に更新しますので、SQLのようにWhere条件とか考慮せずに記述できるので楽です。

また、このコードだと、カレントレコードが先頭から最後まで移動しながら更新していきます。
レコード移動させたくない場合は、Set rs = Me.Recordsetの部分をSet rs = Me.RecordsetCloneとします。こうすればレコード移動せずに更新できます。

ただ、SQLで更新するのと比べると処理速度は遅くなります。ただし、フォームに表示されている件数が多くなければ体験できる差はでないでしょう。

この辺の詳細は興味があれば下記を参照して研究してみてください。

フォームの Recordset, RecorsetClone, RecordSet.Clone の違いとは? - hatena chips

9
beginner 2025/04/17 (木) 08:28:12 61dd6@2128d

hatenaさん ありがとうございます。
それの方が楽に出来そうですね。Recordset、RecordsetCloneも試して様子みます。
あと教えて頂きたいのですが、DAOでレコード追加(rs.AddNew)についてです。
非連結フォームのフィールド値を変数にしてテーブル追加させる場合、そのデータ形式も設定した方がいいのでしょうか? 
例えば下記の右辺がその変数で文字列の場合です。それはどう記述するといいでしょうか? また日付型の場合は?
rs![検査] = kensa
知識不足ですみません、宜しくお願いします。

10

非連結フォームのフィールド値を変数にしてテーブル追加させる場合、そのデータ形式も設定した方がいいのでしょうか? 
例えば下記の右辺がその変数で文字列の場合です。それはどう記述するといいでしょうか? また日付型の場合は?
rs![検査] = kensa

フィールドのデータ型と変数のデータ型は合わせた方がいいでしょう。
ただ、フィールドがNull値を許容する設定で、Null値を代入することがあるならVariant型かな。

日付型の場合は、変数に代入する前に日付として正しいかどうかIsDate関数でチェックするようにした方がいいですね。

11
beginner 2025/04/17 (木) 10:14:25 61dd6@2128d

hatenaさん ありがとうございます。
NullがOKなら変数宣言時に Dim kensa As Variant としておくと rs![検査] = kensa の右辺には特に何もする必用ないという事ですね? この右辺を rs![検査] = ’ & kensa & ' としてもエラーになるので、どう設定するのかなと思ってたのです。
ネットで調べても右辺固定値の場合は型指定がすべきとあるのですがフォーム値の場合は特にデータ型設定してないので、分からなかったのです。DAOは勉強し始めでルールがあまり分かってないもので。

12

NullがOKなら変数宣言時に Dim kensa As Variant としておくと rs![検査] = kensa の右辺には特に何もする必用ないという事ですね? 

はい、フィールドのデータ型がテキスト型の場合、それでほぼ問題ないです。
非連結テキストボックスは未入力の場合はNull値になりますので。
ただ、VBAで "" を代入したり、 "" を手入力した場合、Null値ではなく空文字列になりますので、注意が必要です。
この辺はいろいろ複雑なので説明しだすときりがなくより混乱しそうなので、とりあえずフィールドのプロパティで「空文字列の許可」を「いいえ」にしておいて、エラーが出たらその時に対応するということでいいでしょう。

ネットで調べても右辺固定値の場合は型指定がすべきとあるのですがフォーム値の場合は特にデータ型設定してないので、分からなかったのです。

これは、SQLの文法と、VBAの文法をごっちゃにしているのが、理解が進まない原因でしょう。

SQL文はVBAコード内では単なる文字列です。それをAccessやDAOがSQLとして解釈して実行します。単なる文字列ですから型指定は文字列内で型指定文字(')でします。

例えば下記のように'で囲んで文字列だと指定します。これはSQL文法です。

strSQL = "検査='" & Kensa & "'" 

DAOで更新する場合は、rs![検査] はVBAオブジェクトなのでVBA文法にそって記述します。ですので'で囲こむ必要はないです。

rs![検査].Value = kensa

コードを読むときやネット記事を読むときは、それがSQL文法なのか、VBA文法なのか意識して読むと理解がすすむと思います。

下記の記事もVBA上でSQLを扱う場合の注意点について説明していますので目を通しておくことをお勧めします。

Access上のコード内で引用符(")と単引用符(')の使い分けについて - hatena chips

13
beginner 2025/04/17 (木) 11:59:35 61dd6@2128d

hatenaさん ありがとうございます。
確かにSQLの文法と、VBAの文法がごっちゃになってました。それを意識しながら進めます。多分DAOが何なのかが理解できてないのですね。色々勉強していきます。自分のコードが妥当なのかどうか自信ない事が多いので・・・
今回も大変お世話になりました。