CRUD図(CRUDマトリクス)とは、行に機能、列にテーブルを並べ、交わるセルに「C(作成)・R(参照)・U(更新)・D(削除)」のどれを行うかを書いた表です。 1枚で「どの機能がどのデータを触るか」が見渡せるので、機能の抜けや、誰も作らないデータ、変更の影響範囲を見つけるのに使います。

この記事では、架空の会員制サービスを題材にした10機能×8テーブルの記入例を、Markdown表・CSV・タブ区切りの3形式で載せます。CSV を読み込んで抜けを洗い出す Python コードと実行結果、表計算ソフトの COUNTIF で同じ集計をする数式も付けました。

確認環境:Ubuntu 24.04、Python 3.11(標準ライブラリのみ)。表計算の数式は LibreOffice Calc 24.2 で計算し、Python の結果と同じ値になることを確かめています。

CRUD図の早見表

記号 意味 SQL での操作 例
C Create(作成) INSERT 会員登録で会員の行を作る
R Read(参照) SELECT 予約登録で施設を読む
U Update(更新) UPDATE プロフィール変更で会員を書き換える
D Delete(削除) DELETE 予約取消で予約の行を消す
見る向き 見つかること 対応の例
列(テーブル)を縦に見る:C が無い データを作る機能が書かれていない 管理機能や取込バッチを足す
列を縦に見る:R が無い 作ったデータを誰も読まない 一覧・照会機能を足すか、テーブルの要否を見直す
列を縦に見る:D が無い データが増え続ける 削除・保存期間の方針を決める
列を縦に見る:全部空 どの機能からも使われないテーブル 機能を足すか、テーブルを外す
列を縦に見る:U が多い 更新が集中し、変更の影響が広い 排他制御とテストの範囲を決める
行(機能)を横に見る:全部空 データを使わない機能、または書きもれ 記入もれを確かめる
行を横に見る:記号が多い 1つの機能が多くのテーブルを触る 機能を分けるか、トランザクションの範囲を確かめる

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「CRUD図(CRUDマトリクス)の書き方と例|貼って使えるテンプレート」

CRUD図の書き方:4つの手順

手順 やること 元にする資料
1. 列にテーブルを並べる エンティティまたはテーブルの名前を左から並べる ER図、テーブル一覧
2. 行に機能を並べる 機能IDと機能名を上から並べる 機能一覧
3. セルに C・R・U・D を書く 機能ごとに、触るテーブルと操作を書く 機能の説明、画面や処理の設計
4. 縦と横に見直す 早見表の観点で抜けを探し、機能やテーブルを直す できあがった CRUD図

セルの書き方のルール

書き方をそろえておくと、あとで数式やコードで集計できます。

決めること この記事のルール
文字 半角大文字の C・R・U・D だけを使う
複数の操作 区切りを入れずに C→R→U→D の順で続けて書く(例:RU、CRUD)
操作しないセル 空欄にする
論理削除(削除フラグを立てる) U と書く。行そのものを消すときだけ D と書く
更新前の読み込み 画面に表示する、値を確かめるなどで読むなら RU と書く
粒度 行は機能一覧の1機能、列は1テーブル

記入例:会員制サービスの10機能×8テーブル

題材は、施設の予約ができる架空の会員制サービスです。まず利用者向けの10機能だけを書きます。

Markdown 表

機能 \ テーブル 会員 プラン 申込 予約 施設 お知らせ 問い合わせ 退会理由
F01 会員登録 C R C
F02 ログイン RU
F03 プロフィール変更 RU
F04 プラン変更 RU R CR
F05 施設検索 R
F06 予約登録 R CR R
F07 予約変更・取消 RUD R
F08 お知らせ閲覧 R
F09 問い合わせ送信 R C
F10 退会 RU RU RD

CSV

次の内容を crud.csv という名前で保存します(文字コードは UTF-8)。このあとの Python コードはこのファイルを読み込みます。

機能,会員,プラン,申込,予約,施設,お知らせ,問い合わせ,退会理由
F01 会員登録,C,R,C,,,,,
F02 ログイン,RU,,,,,,,
F03 プロフィール変更,RU,,,,,,,
F04 プラン変更,RU,R,CR,,,,,
F05 施設検索,,,,,R,,,
F06 予約登録,R,,,CR,R,,,
F07 予約変更・取消,,,,RUD,R,,,
F08 お知らせ閲覧,,,,,,R,,
F09 問い合わせ送信,R,,,,,,C,
F10 退会,RU,,RU,RD,,,,

タブ区切り(表計算ソフトに貼る用)

表計算ソフトのセル A1 を選んで貼り付けると、機能が A 列、テーブルが B〜I 列に入ります。

機能  会員  プラン 申込  予約  施設  お知らせ    問い合わせ   退会理由
F01 会員登録    C   R   C                   
F02 ログイン    RU                          
F03 プロフィール変更    RU                          
F04 プラン変更   RU  R   CR                  
F05 施設検索                    R           
F06 予約登録    R           CR  R           
F07 予約変更・取消             RUD R           
F08 お知らせ閲覧                      R       
F09 問い合わせ送信 R                       C   
F10 退会  RU      RU  RD              

空のテンプレート

自分の題材で始めるときは、1行目をテーブル名、1列目を機能名に書き換えて使います。

機能,テーブル1,テーブル2,テーブル3,テーブル4,テーブル5
F01 機能名,,,,,
F02 機能名,,,,,
F03 機能名,,,,,
F04 機能名,,,,,
F05 機能名,,,,,

Python で抜けと集中を洗い出す

CSV を読み込み、テーブルごとに C・R・U・D を書いた機能の数を数えて、早見表の観点に当てはまるテーブルを表示します。

import csv
import sys

path = sys.argv[1] if len(sys.argv) > 1 else "crud.csv"
with open(path, encoding="utf-8-sig", newline="") as f:
    rows = list(csv.reader(f))

tables = rows[0][1:]
functions = [r[0] for r in rows[1:]]
cells = {(r[0], t): v.strip().upper() for r in rows[1:] for t, v in zip(tables, r[1:])}

# テーブルごとに C/R/U/D を書いた機能の数を数える
count = {t: {op: sum(op in cells[(fn, t)] for fn in functions) for op in "CRUD"} for t in tables}

print(f"機能 {len(functions)} 個 x テーブル {len(tables)} 個")
print("\n[テーブルごとの集計]")
print("テーブル\tC\tR\tU\tD")
for t in tables:
    print(t + "\t" + "\t".join(str(count[t][op]) for op in "CRUD"))


def names(cond):
    return [t for t in tables if cond(count[t])] or "なし"


used = lambda c: sum(c.values()) > 0
print("\n[洗い出し]")
print("どの機能からも使われないテーブル:", names(lambda c: not used(c)))
print("C が無いテーブル:", names(lambda c: used(c) and c["C"] == 0))
print("R が無いテーブル:", names(lambda c: used(c) and c["R"] == 0))
print("D が無いテーブル:", names(lambda c: used(c) and c["D"] == 0))

threshold = 3
print(f"更新(U)が {threshold} 機能以上に集中するテーブル:")
for t in tables:
    if count[t]["U"] >= threshold:
        who = [fn for fn in functions if "U" in cells[(fn, t)]]
        print(f"  {t}: {count[t]['U']} 機能 {who}")

print("どのテーブルも使わない機能:", [fn for fn in functions if not any(cells[(fn, t)] for t in tables)] or "なし")
bad = [(fn, t, v) for (fn, t), v in cells.items() if set(v) - set("CRUD")]
print("C/R/U/D 以外の文字があるセル:", bad or "なし")
python3 crud_check.py crud.csv

実行結果です。

機能 10 個 x テーブル 8 個

[テーブルごとの集計]
テーブル    C   R   U   D
会員  1   6   4   0
プラン 0   2   0   0
申込  2   2   1   0
予約  1   3   1   2
施設  0   3   0   0
お知らせ    0   1   0   0
問い合わせ   1   0   0   0
退会理由    0   0   0   0

[洗い出し]
どの機能からも使われないテーブル: ['退会理由']
C が無いテーブル: ['プラン', '施設', 'お知らせ']
R が無いテーブル: ['問い合わせ']
D が無いテーブル: ['会員', 'プラン', '申込', '施設', 'お知らせ', '問い合わせ']
更新(U)が 3 機能以上に集中するテーブル:
  会員: 4 機能 ['F02 ログイン', 'F03 プロフィール変更', 'F04 プラン変更', 'F10 退会']
どのテーブルも使わない機能: なし
C/R/U/D 以外の文字があるセル: なし

結果の読み方

洗い出されたもの テーブル 考えられる理由 次にやること
どの機能からも使われない 退会理由 退会の機能で理由を選ばせる想定が、機能側に書かれていない 退会に R を足す。登録する管理機能も足す
C が無い プラン、施設、お知らせ 運営側が登録するデータで、管理機能がまだ機能一覧に無い 管理機能を機能一覧に足す
R が無い 問い合わせ 送信された問い合わせを読む機能が無い 問い合わせ対応の機能を足す
D が無い 会員、プラン、申込、施設、お知らせ、問い合わせ 消さない方針なのか、決めていないのかが区別できない テーブルごとに保存期間と削除の方法を決める
U が集中 会員(4機能) ログイン日時、プロフィール、プラン、退会の状態を同じテーブルで持っている 同時に更新されたときの扱いを決める。変更時は4機能をテスト対象にする

「C が無い」「D が無い」は誤りとは限りません。 他システムから取り込むデータなら C が無くて正しく、履歴のように消さないデータなら D が無くて正しいです。大切なのは、理由を説明できる状態にすることです。理由が決まったら、CRUD図の欄外に「プラン:管理機能 A01 で登録」「申込:削除しない(履歴として保持)」のように書き残します。

管理機能とバッチを足して、もう一度調べる

洗い出した内容をもとに、管理機能5つ(A01〜A05)とバッチ1つ(B01)を足し、退会に退会理由の R を足しました。

機能,会員,プラン,申込,予約,施設,お知らせ,問い合わせ,退会理由
F01 会員登録,C,R,C,,,,,
F02 ログイン,RU,,,,,,,
F03 プロフィール変更,RU,,,,,,,
F04 プラン変更,RU,R,CR,,,,,
F05 施設検索,,,,,R,,,
F06 予約登録,R,,,CR,R,,,
F07 予約変更・取消,,,,RUD,R,,,
F08 お知らせ閲覧,,,,,,R,,
F09 問い合わせ送信,R,,,,,,C,
F10 退会,RU,,RU,RD,,,,R
A01 プラン管理,,CRUD,,,,,,
A02 施設管理,,,,R,CRUD,,,
A03 お知らせ管理,,,,,,CRUD,,
A04 問い合わせ対応,R,,,,,,RU,
A05 退会理由マスタ管理,,,,,,,,CRUD
B01 退会会員データ削除,RD,,RD,,,,RD,
python3 crud_check.py crud_admin.csv
機能 16 個 x テーブル 8 個

[テーブルごとの集計]
テーブル    C   R   U   D
会員  1   8   4   1
プラン 1   3   1   1
申込  2   3   1   1
予約  1   4   1   2
施設  1   4   1   1
お知らせ    1   2   1   1
問い合わせ   1   2   1   1
退会理由    1   2   1   1

[洗い出し]
どの機能からも使われないテーブル: なし
C が無いテーブル: なし
R が無いテーブル: なし
D が無いテーブル: なし
更新(U)が 3 機能以上に集中するテーブル:
  会員: 4 機能 ['F02 ログイン', 'F03 プロフィール変更', 'F04 プラン変更', 'F10 退会']
どのテーブルも使わない機能: なし
C/R/U/D 以外の文字があるセル: なし

抜けの指摘が無くなり、残ったのは会員テーブルへの更新の集中だけになりました。これは設計上の注意点として、排他制御やテスト範囲の検討に引き継ぎます。

表計算ソフトの COUNTIF で同じ集計をする

タブ区切りの記入例をセル A1 に貼り付けた状態(データが B2〜I11)で、次の数式を B 列に入れ、I 列まで右へコピーします。

セル A列に書く見出し B列に入れる数式 意味
B12 Cの数 =COUNTIF(B$2:B$11,"*C*") C を含むセルの数
B13 Rの数 =COUNTIF(B$2:B$11,"*R*") R を含むセルの数
B14 Uの数 =COUNTIF(B$2:B$11,"*U*") U を含むセルの数
B15 Dの数 =COUNTIF(B$2:B$11,"*D*") D を含むセルの数
B16 使う機能の数 =COUNTA(B$2:B$11) 空欄でないセルの数
B17 確認 =IF(B16=0,"未使用",IF(B12=0,"Cなし","")&IF(B15=0,"Dなし","")&IF(B14>=3,"U集中","")) 当てはまる観点を文字で表示
J2 (J1 に「使うテーブルの数」) =COUNTA(B2:I2) その機能が触るテーブルの数。J11 まで下へコピー

検索条件の "*C*" は「C を含む」という意味のワイルドカードです。セルに RU や CRUD のように複数の文字が入っていても、1セルとして数えられます。

LibreOffice Calc 24.2 で計算した結果です(12〜17行目)。数式を入れた .xlsx 形式のファイルを開いて計算しています。

見出し 会員 プラン 申込 予約 施設 お知らせ 問い合わせ 退会理由
Cの数 1 0 2 1 0 0 1 0
Rの数 6 2 2 3 3 1 0 0
Uの数 4 0 1 1 0 0 0 0
Dの数 0 0 0 2 0 0 0 0
使う機能の数 7 2 3 3 3 1 1 0
確認 DなしU集中 CなしDなし Dなし CなしDなし CなしDなし Dなし 未使用

Python の [テーブルごとの集計] と同じ値です。J 列(使うテーブルの数)は F01 から順に 3、1、1、3、1、3、2、1、2、3 になりました。数が大きい機能は、複数のテーブルをまとめて書き換えるので、途中で止まったときに元へ戻せるか(トランザクションの範囲)を確かめる対象になります。

画面×テーブル・バッチ×テーブルへの広げ方

行を「機能」から別の単位に替えると、同じ形の表で別の確認ができます。列(テーブル)と書き方のルールはそのまま使えます。

行の単位 列 わかること 作る時期の目安
機能 テーブル(エンティティ) 機能とデータの抜け 要件定義〜基本設計
画面 テーブル 画面を直すとき、またはテーブルを直すときの影響範囲 基本設計
バッチ テーブル バッチどうし、バッチと画面が同じテーブルを書き換える時間帯の重なり 基本設計〜詳細設計
API テーブル 呼び出し元ごとのデータの使い方 基本設計〜詳細設計
機能 テーブルの項目(列) 項目ごとの入力元と使われ方 詳細設計(重要なテーブルに絞る)

画面×テーブルの例

1つの機能が複数の画面に分かれるときは、画面ごとに行を分けます。

画面 \ テーブル 会員 プラン 申込 予約 施設
S01 会員登録入力 R
S02 会員登録確認 C R C
S03 マイページ R R R R
S04 施設一覧 R
S05 予約入力 R CR R
S06 予約詳細 RUD R

機能×テーブルでは「F01 会員登録」の1行だったものが、入力画面(プランを読むだけ)と確認画面(会員と申込を作る)に分かれました。会員テーブルに項目を足すとき、見直す画面は S02・S03・S05 の3つだとわかります。

バッチ×テーブルの例

バッチは、実行のタイミングを列に足すと使いやすくなります。

バッチ \ テーブル 実行 会員 申込 予約 お知らせ 問い合わせ
B01 退会会員データ削除 毎日 2:00 RD RD RD
B02 予約リマインド送信 毎日 8:00 R R
B03 終了した予約の整理 毎月1日 2:30 RD
B04 公開期限切れお知らせ削除 毎週日曜 4:00 RD

バッチの D は、機能×テーブルで「D が無い」と出たテーブルの答えになることが多いです。B01 と B03 のように近い時間帯に動くバッチが同じテーブルを書き換えないか、画面からの更新と重ならないかも、この表で確かめられます(この例では B01 と B03 が触るテーブルは重なっていません)。

よくあるつまずきと直し方

つまずき 起きること 直し方
「○」だけを書く 読むだけなのか書き換えるのかがわからない 必ず C・R・U・D の文字で書く
全角・小文字・区切り文字が混ざる 数式やコードで数えられない 半角大文字で続けて書くルールにそろえる
機能の粒度がそろっていない 行によって記号の多さが大きく違う 機能一覧の1機能を1行にする。大きい機能は画面×テーブルで分ける
論理削除を D と書く 行が消えると読み取られる U と書き、欄外に「退会は状態の更新」と注記する
一度作って更新しない 設計と表がずれていく 機能やテーブルを足したら CSV を直し、コードを実行し直す

よくある質問

Q. CRUD図は、いつ作るのがよいですか? A. 機能一覧とエンティティの一覧がそろった時点(要件定義の終わり〜基本設計の始め)が作りやすいです。早い段階ではエンティティ単位でおおまかに作り、テーブルが決まったら列を置き換えます。

Q. ER図とは何が違いますか? A. ER図はデータどうしの関係を、CRUD図は機能とデータの関係を表します。ER図でテーブルを洗い出し、CRUD図で「そのテーブルを作る機能・消す機能があるか」を確かめる、という順で組み合わせます。

Q. 行と列はどちらに機能を置きますか? A. どちらでも表せます。機能のほうが数が多くなりやすいので、縦に長くできる行に機能を置くと、画面でも印刷でも読みやすくなります。

Q. テーブルが数十個あるときはどうしますか? A. 会員まわり、予約まわりのように業務のまとまりで表を分けます。CSV で管理しておけば、この記事のコードでまとまりごとに集計できます。

まとめ

  • CRUD図は「機能×テーブル」に C・R・U・D を書いた表。機能とデータの抜けを見つけるために作る
  • セルは半角大文字で C→R→U→D の順に続けて書くと、数式やコードで集計できる
  • 列を縦に見て、C が無い・R が無い・D が無い・全部空・U が多いテーブルを探す
  • 「無い」は誤りとは限らない。理由を説明できる状態にして、欄外に書き残す
  • 10機能×8テーブルの例では、使われないテーブル1・Cなし3・Rなし1・Dなし6・U集中1が見つかり、管理機能とバッチを足すと抜けの指摘が無くなった
  • 表計算ソフトでは =COUNTIF(範囲,"*C*") で同じ集計ができる
  • 行を画面・バッチ・APIに替えると、影響範囲や実行時間帯の重なりも確かめられる

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「CRUD図(CRUDマトリクス)の書き方と例|貼って使えるテンプレート」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?