連投になりすみません。ここ最近投稿した関連です。
テーブル: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
下記のようにデバッグ用のコードを埋め込んで実行してみてください。
イミディエイトウィンドウにstrSQLに格納されているSQL文が出力されますので、それが正しいものになっているか確認しましょう。
状況の追記です。この一覧フォームのフィルター用コンボボックスには製品ID、顧客ID、完成年、完成月、完成日があります。製品IDが未選択時はリスト表示出来ない様にしてます。各コンボ選択(更新後)ではフィルター(投稿したコードを各コンボからCall)が効いてフォームのリスト表示は問題ありません。リスト表示分のみcheckをYesに更新させるボタン(投稿したコード)で更新させる形です。その後、DoCmd.RunSQL strSQL を CurrentDb.Execute strSQL にして試しして下さいみると製品IDと顧客IDは問題なくフォーム表示分のみYesになります。完成年コンボで抽出後ではフォーム表示は問題ないのですがYesに更新実行すると”実行エラー3061 パラメータが少なすぎます。1を指定して下さい”となります(前回投稿と同様)。因みにコンボ完成月、完成日は完成年が未選択では不可にしてます。DoCmd.RunSQL strSQL では実行されるのですが完成年条件が無視されて指定の製品ID分の全レコードがcheck Yesになります。
何かコードに不備があるのでしょうか?
とりあえず上の回答のデバッグコードを試してみて、出力されたSQL文を提示してください。
あと、下記を確認ください。
フォームのレコードソースは「T_製造履歴」で間違いないですか。
「完成年」フィールドのデータ型は数値型ですか。
hatenaさん ありがとうございます。
試した分のイミディエイトウィンドウには UPDATE [T_製造履歴] SET [check] = -1 WHERE ([製品ID]=377 AND [完成年]=2015); と表示されました。
フォームのレコードソースは「T_製造履歴」を基にして他のテーブルを連結させたクエリとしてます。
「完成年」フィールドは日付型の完成日がT_製造履歴にあり、それを使ってYear([完成日付])をそのクエリに組み込んでます。フォームの抽出コンボ完成年のソースも同じクエリから取ってきてます。
[T_製造履歴]テーブルには[完成年]フィールドはないということですよね。
ならば、[完成年]はパラメータとして判断されますので、
”実行エラー3061 パラメータが少なすぎます。1を指定して下さい”
というエラーになります。
SQLの[T_製造履歴]の部分をフォームのレコードソースのクエリに変更するか、
下記のようにサブクエリにして[完成年]、[完成月]の演算フィールドを追加すればいいでしょう。
ちなみに、「WHEREのMe.filterが効いてなく全レコードがチェックON」になるのは、
Accessは[完成年]をパラメータとして判断して、まずはアクティブフォームから探します。
フォームには[完成年]というフィールドがありますから、その値をパラメータ値として取得します。フォームの[完成年]を参照するとカレントレコードの値を取得します。
カレントレコードの完成年が2025年で、[cb完成年]で2025年を選択していればすべてのレコードの[check]がオンになります。
hatenaさん ありがとうございます。
UPDATE [フォームソースのクエリ名] にしたら正常に対象分(表示されているレコードリスト)のみYesに出来ました!
クエリを更新する事でソースであるテーブルを更新させてもいいのですね。
[T_製造履歴]には完成年フィールドがなかったのでフィルターが出来なかったのですね。前回投稿と同じ様な事例でした。
まだ完全には理解出来てないと思いますがパラメータになってしまうというのが大分イメージ出来ました。
自分では絶対解決出来ない事でしたので本当に助かりました。
因みにフォームの表示分を対象に色々する場面が多いのですが、SQL使わずDAOだけでも可能でしょうか? DAOは余り理解出来てないのですがフォームの表示レコードをレコードセットにするイメージで。
フォームに表示されているレコードはRecordsetプロパティで取得できますので、それを対象に更新すればいいですね。
この方法はフォームに現在表示されているレコードを対象に更新しますので、SQLのようにWhere条件とか考慮せずに記述できるので楽です。
また、このコードだと、カレントレコードが先頭から最後まで移動しながら更新していきます。
レコード移動させたくない場合は、
Set rs = Me.Recordset
の部分をSet rs = Me.RecordsetClone
とします。こうすればレコード移動せずに更新できます。ただ、SQLで更新するのと比べると処理速度は遅くなります。ただし、フォームに表示されている件数が多くなければ体験できる差はでないでしょう。
この辺の詳細は興味があれば下記を参照して研究してみてください。
フォームの Recordset, RecorsetClone, RecordSet.Clone の違いとは? - hatena chips
hatenaさん ありがとうございます。
それの方が楽に出来そうですね。Recordset、RecordsetCloneも試して様子みます。
あと教えて頂きたいのですが、DAOでレコード追加(rs.AddNew)についてです。
非連結フォームのフィールド値を変数にしてテーブル追加させる場合、そのデータ形式も設定した方がいいのでしょうか?
例えば下記の右辺がその変数で文字列の場合です。それはどう記述するといいでしょうか? また日付型の場合は?
rs![検査] = kensa
知識不足ですみません、宜しくお願いします。
フィールドのデータ型と変数のデータ型は合わせた方がいいでしょう。
ただ、フィールドがNull値を許容する設定で、Null値を代入することがあるならVariant型かな。
日付型の場合は、変数に代入する前に日付として正しいかどうかIsDate関数でチェックするようにした方がいいですね。
hatenaさん ありがとうございます。
NullがOKなら変数宣言時に Dim kensa As Variant としておくと rs![検査] = kensa の右辺には特に何もする必用ないという事ですね? この右辺を rs![検査] = ’ & kensa & ' としてもエラーになるので、どう設定するのかなと思ってたのです。
ネットで調べても右辺固定値の場合は型指定がすべきとあるのですがフォーム値の場合は特にデータ型設定してないので、分からなかったのです。DAOは勉強し始めでルールがあまり分かってないもので。
はい、フィールドのデータ型がテキスト型の場合、それでほぼ問題ないです。
非連結テキストボックスは未入力の場合はNull値になりますので。
ただ、VBAで
""
を代入したり、""
を手入力した場合、Null値ではなく空文字列になりますので、注意が必要です。この辺はいろいろ複雑なので説明しだすときりがなくより混乱しそうなので、とりあえずフィールドのプロパティで「空文字列の許可」を「いいえ」にしておいて、エラーが出たらその時に対応するということでいいでしょう。
これは、SQLの文法と、VBAの文法をごっちゃにしているのが、理解が進まない原因でしょう。
SQL文はVBAコード内では単なる文字列です。それをAccessやDAOがSQLとして解釈して実行します。単なる文字列ですから型指定は文字列内で型指定文字(
'
や”
)でします。例えば下記のように
'
で囲んで文字列だと指定します。これはSQL文法です。DAOで更新する場合は、
rs![検査]
はVBAオブジェクトなのでVBA文法にそって記述します。ですので'
で囲こむ必要はないです。コードを読むときやネット記事を読むときは、それがSQL文法なのか、VBA文法なのか意識して読むと理解がすすむと思います。
下記の記事もVBA上でSQLを扱う場合の注意点について説明していますので目を通しておくことをお勧めします。
Access上のコード内で引用符(")と単引用符(')の使い分けについて - hatena chips
hatenaさん ありがとうございます。
確かにSQLの文法と、VBAの文法がごっちゃになってました。それを意識しながら進めます。多分DAOが何なのかが理解できてないのですね。色々勉強していきます。自分のコードが妥当なのかどうか自信ない事が多いので・・・
今回も大変お世話になりました。