Accessで曜日、週、月などの条件で繰り返しを設定できるリマインダー機能を作成しようとしています。
指定の条件で日付を抽出するためのテーブルとクエリの構成で行き詰まってしまい、質問させていただきます。
こちらではじめてお世話になります。Accessのバージョンは2013,2016,office365です。
サンプルとしてできるだけ問題部分を単純にしたテーブルとクエリを作成しました。
テーブル構成
Tリマインダー
Tくり返し条件
T日付
※テーブルのフィールドと内容について画像を添付します。
作成したクエリ
Qクロス結合 … 「Tリマインダー」と「T日付」をクロスさせて、式で検索用フィールドを追加したクエリ
SELECT TリマインダーID.[リマインダーID], T日付.日付, Weekday([日付]) AS 曜日番号, (Day([日付])+6)\7 AS 週番号
FROM T日付, TリマインダーID;
Q抽出結果 … 「Qクロス結合」からリマインダーIDごとに条件に合う日付を抽出するクエリ
SELECT Qクロス結合.[リマインダーID], Qクロス結合.日付, Qクロス結合.曜日番号, Qクロス結合.週番号
FROM Qクロス結合
WHERE (((Qクロス結合.曜日番号) In (select 曜日番号 from T条件 where T条件.リマインダーID = Qクロス結合.リマインダーID)) AND ((Qクロス結合.週番号) In (select 週番号 from T条件 where T条件.リマインダーID = Qクロス結合.リマインダーID)))
ORDER BY Qクロス結合.[リマインダーID], Qクロス結合.日付;
結果
このクエリでは、リマインダーIDの1と2の条件については希望の結果が得られました。
しかし、リマインダーIDの3の条件のときは週番号の指定がないため何も抽出されませんでした。
質問したい内容
①「Q抽出結果」の各フィールドのwhere条件のIn句の中に抽出する条件が無いときに(または0のみのとき)
そのフィールドでのフィルタリングをせず、全件抽出するようにしたいのですが、どのような方法が考えられますか。
②Tリマインダーにあるような任意の条件で日付の抽出できるよりシンプルまたは負荷の少ない方法はありますか。
あるとすればどのような方法か教えて下さい。
(実際に組み込む際は曜日と週以外にも日付、月など同様の方法で条件を増やすことを想定しています。)
以上、やや個別的な内容になり申し訳有りませんが、ご教授いただきたくよろしくおねがいします。
質問者です。
テーブルの名前は
Tくり返し条件× → T条件○
でした。
大変失礼致しました。
よろしくお願い致します。
質問内容 1, 2 両方ともに関連することですが
Tくり返し条件 テーブルの
データ構成に問題があるでしょう。
一意のリマインダーIDを構成する要素は
週番号 + 曜日番号
なのですから
週番号だけ とか、曜日番号だけ という
どちらか1つだけを表現した行を作ってはならないのです。
rookjimさんの例で申し上げるなら
リマインダーID:3 の「 毎週月曜と金曜 」というのは
・ 週番号は 1 または 2 または 3 または 4 または 5
且つ
・ 曜日番号は 2 または 6
であるのに
週番号の入力値を 0 という非該当の数字で表現していることが
SQL文が複雑になり、思ったように抽出できない原因になっています。
T条件
このように
複数の条件( 週 + 曜日 )を全て網羅するようなデータ構成に変更すると
SQL文は以下のように簡潔に記述できて
抽出漏れも解消できます。
mayuさん
つたない質問内容を読み取っていただき、丁寧な回答いただきありがとうございます。
ご指摘いただき、T条件のテーブルの記述の仕方が、望む抽出結果を得るために適さない形式であることがわかりました。
T条件のテーブルが1レコードごとに1条件の構成になっている理由は、ユーザーがくり返し条件を入力するためのフォームを下のように作成したためです。
ご回答いただいたように複数の条件を全て網羅するようにテーブル構成を変更すると、
「月」、「日付」等の並列する条件を追加した際に条件テーブルが長大なものになってしまうため➀については現在のテーブル構成のまま目的の抽出結果を得られる方法を求めております。引き続きご教授いただけますと幸いです。
または
・条件を網羅したテーブルのデータが作成できるようにフォームを再設計する
・T条件のデータを複数の条件を全て網羅するように変換するようクエリを作成する
等で解決できるかと思うのですが、こちらについても具体的な方法をアドバイスいただけますと幸いです。
検討いただき大変有り難く思っております。大変わがままな質問で恐縮ですが
何卒宜しくお願いします。
「月」や「日付」の条件をテーブルに追加して長大になるというのは
意味がわかりかねます。
前回の回答で、私はフィールド構成の変更を提案したわけではなく
データの持ち方を変更するよう、示唆したに過ぎません。
したがって、ご自身が考える「 長大にならない 」設計方針と
「月」や「日付」をどのような構成でデータ登録し、
週次や曜日毎に実施するデータと同居させる予定なのか
また、どのような SQL で抽出を行うつもりでいるのか、構想をお聞かせ願えますか。
現状の構成は、以下の問題点が明白ですので
・ 第三者から見て、単一の行からテーブルのデータ構造が把握できない
・ 相関サブクエリというのは、莫大な演算回数が発生するパフォーマンスの悪い SQL である
・ 複雑なサブクエリを複数回発行しても、思ったようにデータが抽出できていない
・ 演算フィールドを左辺に指定して抽出条件を指定しているため、インデックスが効かない
運用も構築も この先 地獄を見る覚悟がおありということでしたら
一応、ご希望の回答はしておきます。
なお、週番号だけを指定するリマインダーが あるのかどうか不明ですが
とりあえず、どちらでもいいように冗長な SQL にしておきます。
以下の結果を ユニオンクエリで rookjimさん記載の SQL と結合すればいいでしょう。
具体的なアドバイスをご希望なら、まずは データベースソフトの扱いにおいて
rookjimさんの方針や考え方は、順番や方向性が真逆である
ということを理解いただかない限り、不毛なやりとりになるでしょう。
のではなく、構築手順は
1. 条件を網羅した適切な構造のテーブルを設計する
2. パフォーマンスや見通しの良い SQL を考える
3. VBAの利用やコントロールの配置は必要最小限に抑え、
シンプルで保守性の高いインターフェース( フォーム・レポート )の構築を目指す
になります。
添付画像における リマインダーID: 1 の内容においても
発生条件が「 第2、第4土曜日 」ですから
フォーム上で [ 指定日 ] [ 日 ] といった項目を ユーザが指定する必要は無いように思いますが
代替値を入力しない限り、テーブル上でそれらの項目は Null になります。
これらの項目を SQLでどう扱うか、見通しが立っていないのでしたら
テーブルの設計から見直す必要があり、当然、フォームも改変を余儀なくされます。
まずは、テーブル設計からやり直す気が有るのか否か、意思表示をお願いします。
mayuさん
大変に熱のこもったご回答をいただきありがとうございます。
ひとまず、中段にご提示いただいた、元のクエリで抽出されないレコードを集計クエリで別途抽出しユニオンクエリで結合するという考え方を参考にさせていただき、
実際の動作を検証してみたいと思います。
また、独学なため実例を見る機会があまりなく、テーブル設計含めより適した方法を引き続き検討していきたいと思いますので、機会がありましたらぜひよろしくお願い致します。以上、ありがとうございました。
テーブルの構造を変更する場合のサンプルを以下に載せておきます。
まず、リマインダーは
・ 日 単位で発生するもの
・ それ以外のもの( 週・曜日 )
の2種に大別できるでしょう。
( 両立しない条件のため )
よって、リマインダの定義テーブルに
種別を管理するフィールドが必要になります。( ※1 )
次に、リマインダーの発生周期は
月、週、曜日、日の組み合わせによることから
条件テーブルに、これらの値を格納するフィールドが必要 ( ※2 )
条件テーブルへデータを登録するにあたり、
行数が増えることに漠然とした不安を感じておられるようなので
行数を抑制できるフィールド構成にします ( ※3 )
特定の日に1度きりで実施されるリマインダと
特定の周期で何度も実施されるリマインダは
入力項目を共通化して統一し、Nullや意味不明の値は許可しない ( ※4 )
※1 ~ ※4 を網羅したテーブル定義
※ 続き
データ例
< T_リマインダー >
リマインダーの種別において
・ 日 単位で発生するもの は 1 を入力
・ それ以外のもの ( 週・曜日 ) は 2 を入力
というルールにします
< T_条件 >
F1 ---> 条件ID F5 ---> 週番号_from
F2 ---> リマインダーID F6 ---> 週番号_to
F3 ---> 対象月_from F7 ---> 曜日番号_from
F4 ---> 対象月_to F8 ---> 曜日番号_to
F9 ---> 日番号
※2
月、週、曜日において
連続した値( 毎月、毎週、火~金、4月~6月 など )は
連続した範囲として1行で表現し、
連続しない場合は行を分け、データを個別に登録します。
この部分が、データ登録における一番の難関だと思いますが
データサンプルをよくご覧になれば、構造を理解できるでしょう。
※3
日単位で発生するリマインダにおいて
日番号フィールドに入力する値は、月末は 0
それ以外は 日の数字
※4
曜日や週単位で発生するリマインダにおいて
日番号フィールドに入力する値は 0
※ 続き
結果 ( 一部抜粋 )
mayuさん
テーブル設計を見直したサンプルのご提示ありがとうございます。
素朴な質問のつもりでしたが、ここまで懇切丁寧に回答いただき、
情報量にただただ圧倒される次第です。
また、その前段にてmayuさんよりご提示いただいた、
SUM集計関数を用いて元のクエリで抽出できなかったレコードを一括で抽出し
別途対処するというアイディアを参考にして下のクエリを作成しました。
こちらで一応望む結果を得ることができましたので、あまりスマートな方法では
ないと自覚していますが、ひとまずこの質問は解決にしようと思います。
以上、大変参考になるご回答数々いただき感謝致します。ありがとうございました。