「同じ従業員のEAHとPAHの2行を合計して、EmpID=24なら26.1を1行で表示したい」。Microsoft Accessで勤怠や工数を管理していると、こんなニーズはとてもよく出てきます。本記事では、集計クエリ(Totals)だけで実現する最短ルートと、実務でハマりやすい落とし穴・設計の考え方まで、Access初心者~中級者向けに丁寧に解説します。
Accessで「従業員ごとにUnitsを合算して1行に集約する」とは
まずは、問題のイメージを具体的に整理します。たとえば、次のような勤怠・工数明細テーブル(あるいはクエリ)を考えます。
| EmpID | EmpName | Category | Units |
|---|---|---|---|
| 24 | 山田太郎 | EAH | 8.0 |
| 24 | 山田太郎 | PAH | 18.1 |
| 25 | 佐藤花子 | EAH | 5.0 |
| 25 | 佐藤花子 | PAH | 3.5 |
このように、1人の従業員(EmpID)に対して複数行の明細がある状態から、次のような「従業員ごとの合計」を1行にまとめたい、というのが今回のゴールです。
| EmpID | TotalUnits |
|---|---|
| 24 | 26.1 |
| 25 | 8.5 |
つまり、EmpID=24ならEAHとPAHのUnitsを合計して「26.1」を1行で出したい、という要求です。ここでやるべきことはたった1つ。
- EmpIDごとにUnitsの合計を計算する「集計クエリ」を作る
Accessでは、これを実現するために以下の2パターンが使えます。
- SQLビューで直接クエリを書く(手早く・確実)
- デザインビューで集計クエリ(Totals)を作る(GUI操作)
最小構成:集計クエリでEmpIDごとに合計するだけでOK
まず押さえておきたいのは、「必要最低限の列だけでシンプルな集計クエリを作る」という方針です。特にAccess初心者は、
- EmpID
- EmpName
- 所属部署
- Category
- Units
のように、いろいろな列をそのままクエリに並べたくなります。しかし、集計時点では「EmpID」と「Units」だけを扱うのが正解です。その他の列は、後から別クエリで「結合して付け足す」ほうが、安全で再利用性も高くなります。
SQLビューで作成する(最短・確実な方法)
まずはSQLビューで書くパターンです。すでに勤怠明細を集約する元クエリとして qryAllAllowedHours があると仮定して、以下のように書きます。
SELECT
EmpID,
SUM(Nz(Units, 0)) AS TotalUnits
FROM qryAllAllowedHours
GROUP BY EmpID;
ポイントを整理しましょう。
- EmpID :グループ化のキー(従業員ごと)
- SUM(Nz(Units, 0)) :Unitsを合計。Nullは0として扱う
- GROUP BY EmpID:EmpIDごとに行を集約する
Nz(Units, 0) を使うことで、UnitsがNullになっているレコードがあっても、合計がNullにならず0として扱われます。勤怠データでは、
- 未入力レコード
- 特定区分ではUnitsが入らない
といった理由でNullが含まれることは珍しくないので、安全策として必ずNzでラップしておくのがおすすめです。
並び順も指定したい場合は、最後に ORDER BY EmpID; を付け足します。
SELECT
EmpID,
SUM(Nz(Units, 0)) AS TotalUnits
FROM qryAllAllowedHours
GROUP BY EmpID
ORDER BY EmpID;
このクエリを実行すると、冒頭で示したように
| EmpID | TotalUnits |
|---|---|
| 24 | 26.1 |
| 25 | 8.5 |
という形で、EmpIDごとにUnitsが集計され、従業員1人につき1行だけが残るようになります。
デザインビューで作成する(GUIでの手順)
SQLが苦手な場合や、Accessの標準的な操作で揃えたい場合は、デザインビューで「集計(Σ)」を使って同じクエリを作成できます。
手順は次の通りです。
- ナビゲーションウィンドウで qryAllAllowedHours(または対象テーブル)を右クリックし、「デザインビュー」を開く
- リボンの「集計(Σ)」ボタンをクリックする
- フィールド行に
- 1列目:EmpID
- 2列目:Units
- 「集計」行を次のように設定
- EmpID列:「グループ化」
- Units列:「合計」
- 他の列は追加しない(ここが重要)
- 実行ボタン(赤い「!」)をクリックして結果を確認
「集計」行に「グループ化」「合計」と設定することで、先ほどのSQL
SELECT EmpID, SUM(Units) AS 合計Units
FROM ...
GROUP BY EmpID;
と同じ動作になります。ちなみに、Units列を右クリックして「プロパティ」を開き、「キャプション」に「TotalUnits」などと設定しておけば、出力列名も分かりやすくなります。
ここでの最大のポイントは、この集計クエリにはEmpIDとUnits以外の列を入れないことです。従業員名や部署名などを入れたくなりますが、それをすると行が分割されてしまい、意図通り「EmpID1人につき1行」になりません。
従業員名など他の列を表示したいときの正しいやり方
現場では、「EmpIDだけだと誰のことかわかりにくいから、従業員名や部署も一緒に表示したい」という要望がほぼ必ず出ます。ここでやってはいけないのが、集計クエリの中にEmpNameや部署名を直接入れてしまうことです。
正しい設計は、次の2段階に分けることです。
- EmpID単位の集計クエリ(EmpID + TotalUnitsだけ)を作る
- その集計結果を、従業員マスタなどのテーブルとJOINして、名前や部署を付け足す
SQLで書くとイメージしやすくなります。
/* ① EmpIDごとの集計クエリ(サブクエリ) */
SELECT
EmpID,
SUM(Nz(Units, 0)) AS TotalUnits
FROM qryAllAllowedHours
GROUP BY EmpID;
これを qryEmpUnitsSummary という名前で保存しても良いですし、別のクエリの中でサブクエリとして使うこともできます。従業員マスタ tblEmployee があると仮定すると、次のようにJOINします。
SELECT
E.EmpID,
M.EmpName,
M.Department,
E.TotalUnits
FROM
qryEmpUnitsSummary AS E
INNER JOIN tblEmployee AS M
ON E.EmpID = M.EmpID
ORDER BY
E.EmpID;
このように、「集計の世界」と「属性情報の世界」を分けて考えることで、クエリがシンプルかつ安定し、トラブルを起こしにくくなります。
よくある落とし穴と対策
Accessで集計クエリを組むときに、実務でよくハマるポイントを整理しておきます。特に、現場の既存クエリを元に集計を追加しようとすると、知らないうちに集計結果がおかしくなることがあります。
JOINによる重複で合計値が膨らむ問題
元クエリ qryAllAllowedHours が、すでに複数のテーブルをJOINしている場合、JOINの仕方によっては、同じ勤怠明細が複数行に増えてしまうことがあります。その状態でSUMをかけると、当然合計も膨らんでしまいます。
たとえば、次のような状況です。
| EmpID | 明細ID | Category | Units | プロジェクトID |
|---|---|---|---|---|
| 24 | 1001 | EAH | 8.0 | P001 |
| 24 | 1001 | EAH | 8.0 | P001 |
見た目上は同じ明細が2行あり、合計すると本来8.0のはずが16.0になってしまいます。これは、
- プロジェクトと部署の両方にJOINしている
- どちらのテーブルにも同じプロジェクトIDの行が複数ある
といったときに起こりがちです。
対策としては、
- まずは最も素の明細テーブルだけを集計するクエリを作る
- JOINが必要な場合でも、明細テーブル側に主キー(明細ID)を持たせて、JOIN先で重複しないように設計する
どうしても重複を取り除けない場合は、「一意化したサブクエリ」を挟む方法もあります。
SELECT EmpID, SUM(Units) AS TotalUnits
FROM (
SELECT DISTINCT 明細ID, EmpID, Units
FROM qryAllAllowedHours
) AS U
GROUP BY EmpID;
SELECT DISTINCT で明細ID単位に行を一意化した上で、EmpIDごとにSUMすることで、重複による水増しを防ぎます。理想は、そもそもJOINの設計を見直して重複が出ないようにすることですが、既存システムではこのような「防御的な書き方」が役立つケースも多いです。
Unitsのデータ型がテキストで集計できない・おかしくなる
Accessでは、Unitsの型が「テキスト」になっているケースが意外と多くあります。Excelからインポートしたり、最初に適当にテーブルを作ったりした場合などです。
テキスト型のままSUMをすると、次のような問題が起こります。
- 数値に変換できない文字列が混じるとエラーになる
- Nullや空文字がうまく扱えない
根本的な解決はテーブル設計を見直してUnitsを数値型(通貨型、または倍精度Double)に変更することですが、すぐに変えられない場合は CDbl 関数で明示的に数値に変換します。
SELECT
EmpID,
SUM(CDbl(Nz(Units, 0))) AS TotalUnits
FROM qryAllAllowedHours
GROUP BY EmpID;
ここでは、
Nz(Units, 0)でNullを0に変換CDbl(...)で文字列を倍精度の数値に変換
という2段構えでエラーを防いでいます。将来的には、Unitsを通貨型(Currency)またはDouble型にしておくと、集計や小数計算で悩むことが減ります。
特定の区分(EAH/PAHなど)だけ合算したい
今回のように、「EAHとPAHだけを合算したい」という要件もよくあります。その場合は、WHERE句でCategoryを絞り込みます。
SELECT
EmpID,
SUM(Nz(Units, 0)) AS TotalUnits
FROM qryAllAllowedHours
WHERE Category IN ('EAH', 'PAH')
GROUP BY EmpID;
こうすることで、EAHとPAH以外のレコードは集計から除外されます。
さらに、集計対象の区分を柔軟に変更したいなら、パラメータを使うこともできます。
SELECT
EmpID,
SUM(Nz(Units, 0)) AS TotalUnits
FROM qryAllAllowedHours
WHERE Category IN ([区分1], [区分2])
GROUP BY EmpID;
このように書いておくと、クエリ実行時に「区分1」「区分2」を入力するダイアログが表示され、条件をその場で指定できるようになります。
日付範囲(期間)を指定して集計したい
勤怠・工数集計では、「特定の年月」「特定の期間」で集計したいケースも多いはずです。たとえば「2025年4月度のEAH/PAHをEmpIDごとに合算したい」といった場合です。
テーブルに WorkDate(勤務日)または WorkMonth(年月)といった日付フィールドがある場合、WHERE句に期間条件を足すだけで対応できます。
SELECT
EmpID,
SUM(Nz(Units, 0)) AS TotalUnits
FROM qryAllAllowedHours
WHERE
Category IN ('EAH', 'PAH')
AND WorkDate BETWEEN #2025/4/1# AND #2025/4/30#
GROUP BY EmpID;
柔軟に期間を指定したい場合は、パラメータにするのも定番です。
SELECT
EmpID,
SUM(Nz(Units, 0)) AS TotalUnits
FROM qryAllAllowedHours
WHERE
Category IN ('EAH', 'PAH')
AND WorkDate BETWEEN [開始日を入力] AND [終了日を入力]
GROUP BY EmpID;
こうしておくと、クエリ実行のたびに開始日・終了日を入力するだけで、さまざまな期間の集計に使い回すことができます。
フォームやレポートで合計値を活用する方法
EmpIDごとに合算したクエリは、そのままフォームやレポートのレコードソースとして使うことができます。ここでは、よくある使い方をいくつか紹介します。
一覧フォーム(連続フォーム)で従業員ごとの合計を表示
EmpIDごとの合計を一覧で眺めたい場合、次のような流れになります。
- 集計クエリ(たとえば
qryEmpUnitsSummary)を保存 - 「作成」タブ →「空白のフォーム」または「フォームウィザード」を使って、レコードソースに
qryEmpUnitsSummaryを設定 - フォームの既定ビューを「連続フォーム」にする
- テキストボックスを配置して、EmpIDやTotalUnitsを表示
従業員名や部署もつけ足したい場合は、すでに説明したように、qryEmpUnitsSummaryと従業員マスタをJOINしたクエリを別途作り、そのクエリをレコードソースにします。
集計レポートで部署ごと・従業員ごとの合計を出す
Accessレポートは、「グループフッターに集計値を表示する」という機能が強力です。EmpIDごとのUnits合計クエリをベースに、部署単位・従業員単位でのレポートを簡単に作ることができます。
流れの一例は次の通りです。
- まず、従業員マスタとEmpID集計クエリをJOINしたクエリ(EmpID, EmpName, Department, TotalUnitsなどが出る)を作る
- そのクエリを元にレポートウィザードを実行
- レポートのグループ化で「Department(部署)」→「EmpID」の順にグループを設定
- グループフッターで、部署単位の合計など必要な集計を追加
この構造にしておけば、明細テーブル側の設計やクエリが変更されても、「EmpIDごとの合計クエリ」さえ正しく更新しておけば、レポート側はそのまま使い回せるというメリットがあります。
DSumでその場計算するのはアリ?
Accessでは、フォームやレポートのテキストボックスに DSum 関数を書いて「従業員ごとの合計」をその場で計算することもできます。
=DSum("Units", "qryAllAllowedHours", "EmpID=" & [EmpID])
ただし、DSumは1行ずつ問い合わせを投げて計算するため、データ件数が増えると極端に重くなるのが欠点です。
- 一覧表示(多くの行を同時に表示) → 集計クエリを事前に作っておく方が圧倒的に高速
- 単票フォームで、1人分だけを都度計算する → DSumでも実用的なことが多い
基本的な方針としては、「一覧・レポート → 集計クエリ」「単票でちょっとだけ → DSum」と覚えておくと良いでしょう。
再利用しやすい「EmpID集計クエリ」を設計するコツ
一度「EmpIDごとのUnits合計クエリ」を作ると、実務では次のような使い回しが発生します。
- Monthごとの集計
- 部署ごとの集計
- 残業時間だけの集計
- 特定プロジェクトへのアサイン時間の集計
これらをすべてゼロから書き直していると、クエリだらけになって管理しきれなくなります。そこで、「コアとなるEmpID集計クエリ」を1つ作り、その上に条件付きのクエリを積み重ねる設計がおすすめです。
ベースクエリを分割する
たとえば次のようなレイヤー構造を考えます。
| レイヤー | クエリ例 | 内容 |
|---|---|---|
| レベル1 | qryBaseHours | 生の勤怠明細(必要最低限の列+正しいJOIN) |
| レベル2 | qryEmpUnitsSummary | EmpIDごとのUnits合計(この記事の主役) |
| レベル3 | qryEmpUnitsSummary_EAH_PAH | EAH/PAHに絞ったEmpID合計 |
| レベル4 | qryRpt_EmpUnitsByDept | レポート専用:部署・従業員名付きの最終出力 |
こうして階層を分けておくと、上位クエリで条件や表示列が変わっても、レベル2の qryEmpUnitsSummary は変わらず使い続けられます。バグが出たときも、
- ベース明細に問題があるのか
- 集計のロジックに問題があるのか
- レポート専用の加工に問題があるのか
を切り分けて調査できるので、トラブルシューティングが格段に楽になります。
目的別SQLサンプル集(コピペして使えるテンプレート)
最後に、今回の「EmpIDごとのUnits集計」をベースにした目的別のSQLテンプレートをいくつか挙げておきます。自分の環境に合わせてテーブル名やフィールド名を置き換えて、そのままコピペで試してみてください。
もっとも基本的なEmpIDごとの合計
SELECT
EmpID,
SUM(Nz(Units, 0)) AS TotalUnits
FROM qryAllAllowedHours
GROUP BY EmpID
ORDER BY EmpID;
EAH/PAHだけを合算する
SELECT
EmpID,
SUM(Nz(Units, 0)) AS TotalUnits
FROM qryAllAllowedHours
WHERE Category IN ('EAH', 'PAH')
GROUP BY EmpID
ORDER BY EmpID;
月度(YearMonth)単位でEmpIDごとに合計する
勤怠データに WorkMonth(例:「2025-04」)フィールドがある場合の例です。
SELECT
WorkMonth,
EmpID,
SUM(Nz(Units, 0)) AS TotalUnits
FROM qryAllAllowedHours
WHERE Category IN ('EAH', 'PAH')
GROUP BY WorkMonth, EmpID
ORDER BY WorkMonth, EmpID;
このクエリをベースにすれば、「月×従業員」の2軸の集計をExcelピボットのように行うことができます。
一定時間以上の従業員だけを抽出する
たとえば、「EAH+PAHの合計が20時間以上の従業員だけ抽出したい」場合はHAVING句を使います。
SELECT
EmpID,
SUM(Nz(Units, 0)) AS TotalUnits
FROM qryAllAllowedHours
WHERE Category IN ('EAH', 'PAH')
GROUP BY EmpID
HAVING SUM(Nz(Units, 0)) >= 20
ORDER BY SUM(Nz(Units, 0)) DESC;
WHEREは「集計前の行を絞り込む条件」、HAVINGは「集計後のグループを絞り込む条件」と覚えておくと、Access以外のSQLでも役立ちます。
従業員名も一緒に表示したいときのJOIN例
最後に、従業員名や部署を付け足すJOINの例をもう一度整理しておきます。従業員マスタ tblEmployee がある前提です。
SELECT
S.EmpID,
M.EmpName,
M.Department,
S.TotalUnits
FROM
(
SELECT
EmpID,
SUM(Nz(Units, 0)) AS TotalUnits
FROM qryAllAllowedHours
WHERE Category IN ('EAH', 'PAH')
GROUP BY EmpID
) AS S
INNER JOIN tblEmployee AS M
ON S.EmpID = M.EmpID
ORDER BY
S.EmpID;
この形にしておけば、区分条件(EAH/PAH)や期間条件をサブクエリ内で調整するだけで、同じ構造のレポートをどんどん量産できます。
まとめ:EmpIDごとのUnits集計は「シンプルな集計クエリ」が正解
ここまでの内容をまとめると、Accessで「同一従業員(EmpID)のUnitsを合算して1行に集約する」ために押さえるべきポイントは次の通りです。
- 基本は「集計クエリ(Totals)」でEmpIDごとにSUMするだけ
- SQLなら
GROUP BY EmpID+SUM(Nz(Units,0)) - デザインビューなら「集計(Σ)」を押して、EmpID=グループ化、Units=合計
- SQLなら
- 集計クエリにはEmpIDとUnits以外は入れない
- 従業員名・部署などは、後からマスタとJOINして付け足す
- JOINによる重複・Unitsのデータ型・Nullに注意
- 必要に応じて
SELECT DISTINCTやCDbl、Nzを活用
- 必要に応じて
- EAH/PAHだけ・期間指定・閾値指定はWHERE/HAVINGで柔軟に
- フォームやレポートでは、集計クエリをレコードソースにするのが高速で安定
この設計を採用すれば、今回のような「EmpID=24ならEAHとPAHのUnitsを合計して26.1を1行で表示したい」という要件はもちろん、さまざまな勤怠・工数集計に応用できます。まずは、最小構成の集計クエリを1つ作ってみて、そこから条件やJOINを追加していく形で、シンプルかつ強力なAccess集計基盤を育てていきましょう。

コメント