AccessでEmpIDごとのUnitsを合計して1行に集約する方法|集計クエリとSQLの実務解説

「同じ従業員のEAHとPAHの2行を合計して、EmpID=24なら26.1を1行で表示したい」。Microsoft Accessで勤怠や工数を管理していると、こんなニーズはとてもよく出てきます。本記事では、集計クエリ(Totals)だけで実現する最短ルートと、実務でハマりやすい落とし穴・設計の考え方まで、Access初心者~中級者向けに丁寧に解説します。

目次

Accessで「従業員ごとにUnitsを合算して1行に集約する」とは

まずは、問題のイメージを具体的に整理します。たとえば、次のような勤怠・工数明細テーブル(あるいはクエリ)を考えます。

EmpIDEmpNameCategoryUnits
24山田太郎EAH8.0
24山田太郎PAH18.1
25佐藤花子EAH5.0
25佐藤花子PAH3.5

このように、1人の従業員(EmpID)に対して複数行の明細がある状態から、次のような「従業員ごとの合計」を1行にまとめたい、というのが今回のゴールです。

EmpIDTotalUnits
2426.1
258.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;

このクエリを実行すると、冒頭で示したように

EmpIDTotalUnits
2426.1
258.5

という形で、EmpIDごとにUnitsが集計され、従業員1人につき1行だけが残るようになります。

デザインビューで作成する(GUIでの手順)

SQLが苦手な場合や、Accessの標準的な操作で揃えたい場合は、デザインビューで「集計(Σ)」を使って同じクエリを作成できます。

手順は次の通りです。

  1. ナビゲーションウィンドウで qryAllAllowedHours(または対象テーブル)を右クリックし、「デザインビュー」を開く
  2. リボンの「集計(Σ)」ボタンをクリックする
  3. フィールド行に
    • 1列目:EmpID
    • 2列目:Units
  4. 「集計」行を次のように設定
    • EmpID列:「グループ化」
    • Units列:「合計」
  5. 他の列は追加しない(ここが重要)
  6. 実行ボタン(赤い「!」)をクリックして結果を確認

「集計」行に「グループ化」「合計」と設定することで、先ほどのSQL

SELECT EmpID, SUM(Units) AS 合計Units
FROM ...
GROUP BY EmpID;

と同じ動作になります。ちなみに、Units列を右クリックして「プロパティ」を開き、「キャプション」に「TotalUnits」などと設定しておけば、出力列名も分かりやすくなります。

ここでの最大のポイントは、この集計クエリにはEmpIDとUnits以外の列を入れないことです。従業員名や部署名などを入れたくなりますが、それをすると行が分割されてしまい、意図通り「EmpID1人につき1行」になりません。

従業員名など他の列を表示したいときの正しいやり方

現場では、「EmpIDだけだと誰のことかわかりにくいから、従業員名や部署も一緒に表示したい」という要望がほぼ必ず出ます。ここでやってはいけないのが、集計クエリの中にEmpNameや部署名を直接入れてしまうことです。

正しい設計は、次の2段階に分けることです。

  1. EmpID単位の集計クエリ(EmpID + TotalUnitsだけ)を作る
  2. その集計結果を、従業員マスタなどのテーブルと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明細IDCategoryUnitsプロジェクトID
241001EAH8.0P001
241001EAH8.0P001

見た目上は同じ明細が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ごとの合計を一覧で眺めたい場合、次のような流れになります。

  1. 集計クエリ(たとえば qryEmpUnitsSummary)を保存
  2. 「作成」タブ →「空白のフォーム」または「フォームウィザード」を使って、レコードソースに qryEmpUnitsSummary を設定
  3. フォームの既定ビューを「連続フォーム」にする
  4. テキストボックスを配置して、EmpIDやTotalUnitsを表示

従業員名や部署もつけ足したい場合は、すでに説明したように、qryEmpUnitsSummaryと従業員マスタをJOINしたクエリを別途作り、そのクエリをレコードソースにします。

集計レポートで部署ごと・従業員ごとの合計を出す

Accessレポートは、「グループフッターに集計値を表示する」という機能が強力です。EmpIDごとのUnits合計クエリをベースに、部署単位・従業員単位でのレポートを簡単に作ることができます。

流れの一例は次の通りです。

  1. まず、従業員マスタとEmpID集計クエリをJOINしたクエリ(EmpID, EmpName, Department, TotalUnitsなどが出る)を作る
  2. そのクエリを元にレポートウィザードを実行
  3. レポートのグループ化で「Department(部署)」→「EmpID」の順にグループを設定
  4. グループフッターで、部署単位の合計など必要な集計を追加

この構造にしておけば、明細テーブル側の設計やクエリが変更されても、「EmpIDごとの合計クエリ」さえ正しく更新しておけば、レポート側はそのまま使い回せるというメリットがあります。

DSumでその場計算するのはアリ?

Accessでは、フォームやレポートのテキストボックスに DSum 関数を書いて「従業員ごとの合計」をその場で計算することもできます。

=DSum("Units", "qryAllAllowedHours", "EmpID=" & [EmpID])

ただし、DSumは1行ずつ問い合わせを投げて計算するため、データ件数が増えると極端に重くなるのが欠点です。

  • 一覧表示(多くの行を同時に表示) → 集計クエリを事前に作っておく方が圧倒的に高速
  • 単票フォームで、1人分だけを都度計算する → DSumでも実用的なことが多い

基本的な方針としては、「一覧・レポート → 集計クエリ」「単票でちょっとだけ → DSum」と覚えておくと良いでしょう。

再利用しやすい「EmpID集計クエリ」を設計するコツ

一度「EmpIDごとのUnits合計クエリ」を作ると、実務では次のような使い回しが発生します。

  • Monthごとの集計
  • 部署ごとの集計
  • 残業時間だけの集計
  • 特定プロジェクトへのアサイン時間の集計

これらをすべてゼロから書き直していると、クエリだらけになって管理しきれなくなります。そこで、「コアとなるEmpID集計クエリ」を1つ作り、その上に条件付きのクエリを積み重ねる設計がおすすめです。

ベースクエリを分割する

たとえば次のようなレイヤー構造を考えます。

レイヤークエリ例内容
レベル1qryBaseHours生の勤怠明細(必要最低限の列+正しいJOIN)
レベル2qryEmpUnitsSummaryEmpIDごとのUnits合計(この記事の主役)
レベル3qryEmpUnitsSummary_EAH_PAHEAH/PAHに絞ったEmpID合計
レベル4qryRpt_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=合計
  • 集計クエリには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集計基盤を育てていきましょう。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次