Microsoft Access 掲示板

動的に過去数カ月間の平均値を算出するクエリ

7 コメント
views
4 フォロー

お世話になります。

さっそくですが、表題の件でご相談させたください。

テーブルの素データ(労働時間データ)

対象月氏名時間
2018/01/01AAAAA43.2
2018/01/01BBBBB42.1
2018/01/01CCCCC43.2
2018/02/01AAAAA33.6
2018/02/01BBBBB52.3
2018/02/01CCCCC23.8
2018/03/01AAAAA33.1
2018/03/01BBBBB42.5
2018/03/01CCCCC28.8

毎月、追加更新
社員数:約30,000人
対象期間:6ヶ月

クエリ(過去6ヶ月間の平均時間集計)

氏名6ヶ月平均5ヶ月平均4ヶ月平均3ヶ月平均2ヶ月平均当月
AAAAA32.133.435.828.945.6
BBBBB32.133.435.828.945.6
CCCCC32.133.435.828.945.6

※氏名はDistinct表示
※当月日付(2018/12/01など)をパラメタで受け取り、2ヶ月〜6ヶ月間の平均を算出する

現状、○ヶ月平均はD系の演算クエリでやろうと思っていますが、
あまりにパフォーマンスが悪いのいでご相談させていただきました。

演算フィールドの例:

6ヶ月平均: Round(DAvg("[時間]","労働時間データ","氏名='" & [氏名] & "' and 対象年月 between DateAdd('m', -5, #" & [基準日を入力してください] & "#) and #" & [基準日を入力してください] & "#"),1)
美月
作成: 2019/03/26 (火) 12:50:16
最終更新: 2019/03/26 (火) 14:27:56
通報 ...
1
名前なし 2019/03/26 (火) 15:31:46 f2487@0fc80

スマホで書いてるので検証してなくて申し訳ありませんが
単純に今の書き方を踏襲するなら以下でいけるんじゃ無いですかね(年月のカラム名は対象年月が正しいとして)

select
氏名,
round(avg(時間),1) as 6ヶ月平均,
round(avg(iif(対象年月 between DateAdd('m', -4, cdate([基準日を入力してください])) and cdate([基準日を入力してください]),時間,null)),1) as 5ヶ月平均,
(各ヶ月)
from 労働時間データ
where 対象年月 between DateAdd('m', -5, cdate([基準日を入力してください])) and cdate([基準日を入力してください])
group by 氏名

でもこれ対象者のデータが抜けると平均にならない部分が出てくるので、
個人的にはsumして割る方が良いように思います

2

私も名前なしさんと同様のSQLにします。
名前なしさんと同じロジックですが、、、

PARAMETERS 基準日を入力してください DateTime;
SELECT
 氏名,
 Round(Avg(時間),1) AS 6ヶ月平均,
 Round(Avg(IIf(対象月>=DateAdd('m',-4,[基準日を入力してください]),時間,Null)),1) AS 5ヶ月平均,
 Round(Avg(IIf(対象月>=DateAdd('m',-3,[基準日を入力してください]),時間,Null)),1) AS 4ヶ月平均,
 Round(Avg(IIf(対象月>=DateAdd('m',-2,[基準日を入力してください]),時間,Null)),1) AS 3ヶ月平均,
 Round(Avg(IIf(対象月>=DateAdd('m',-1,[基準日を入力してください]),時間,Null)),1) AS 2ヶ月平均,
 Round(Avg(IIf(対象月=[基準日を入力してください],時間,Null)),1) AS 当月
FROM 労働時間データ
WHERE [対象月] Between DateAdd('m',-5,[基準日を入力してください]) And [基準日を入力してください]
GROUP BY 氏名;

変更点。

  • PARAMETERS でデータ型を日付/時刻型に指定することで、日付以外の入力を拒否する。
  • WHERE で絞り込んでいるので、IIfの条件は>=だけでいい。
3
macof 2019/03/26 (火) 16:29:32 修正 f2487@0fc80

(名前入れ忘れてました)
hatena 様
お手数かけて申し訳ありません。
まさにその辺修正しようと思って来たんですがもう直していただいていたとは…
パラメータの型指定もそんな形で出来たんですね。

4

別案

下記のテーブルを作成します。

T_連番

連番
1
2
3
4
5
6

下記のクエリを作成。

Q_月繰り返し

PARAMETERS 基準日を入力 DateTime;
SELECT A.連番, DateAdd("m",1-[B].[連番],[基準日を入力]) AS 対象月
FROM T_連番 AS A, T_連番 AS B
WHERE A.連番>=B.連番;

出力例(パラメータに「2018/06/01」と入力)

連番対象月
62018/01/01
62018/02/01
62018/03/01
62018/04/01
62018/05/01
62018/06/01
52018/02/01
52018/03/01
52018/04/01
52018/05/01
52018/06/01
42018/03/01
42018/04/01
42018/05/01
42018/06/01
32018/04/01
32018/05/01
32018/06/01
22018/05/01
22018/06/01
12018/06/01

このクエリと労働時間データテーブルを結合してクロス集計クエリを作成すれば希望の結果になります。

TRANSFORM Round(Avg([時間]),1) AS 式1
SELECT 労働時間データ.氏名
FROM 労働時間データ INNER JOIN Q_月繰り返し ON 労働時間データ.対象月 = Q_月繰り返し.対象月
GROUP BY 労働時間データ.氏名
ORDER BY Q_月繰り返し.連番 DESC 
PIVOT Q_月繰り返し.連番;
5
美月 2019/03/26 (火) 21:09:46 a63a9@d6973

macof様、hatena様

ありがとうございます。
格段にパフォーマンスがアップいたしました。

ご指摘の通り、データがない場合、
平均値が正しく計算されないですね。
気づきませんでした。、

sumしてデータ件数で割るとした場合、
ゼロもしくはnullでないデータ件数はどう判定したらよいですか?

すみません、別案はまだ私の理解が及ばず試せていません。

6

データがない場合は、どのようにしたいのでしょうか。

例えば、1月、2月、5月、6月 のデータがある(3月、4月がない)場合の6か月平均は、
(1月+2月+5月+6月)/4
ですか。
あるいは、ない月は 0時間として、
(1月+2月+0+0+5月+6月)/6
ですか。

前者なら、Avg関数はNullを無視しますので、前回の回答のSQLでOKなはずです。

後者の場合は、レコード無しだと複雑になるので、入力するときに、労働がない月も 0時間として入力するようにすれば、前回の回答でOKです。

7
美月 2019/03/27 (水) 12:46:14 a63a9@a32b3

hatena様

ありがとうございます。

前者です。Avg関数はnullを無視する仕様知りませんでした。勉強になりました。

これで解決です。どうもありがとうございました。