CH 014つのテーブルで読み解く全体像
ECサイトやスマホ決済を扱うシステムの裏側では、データベースが大量のデータを整理して保存している。といっても構造は意外とシンプルで、ユーザー・店舗・商品・購入履歴の 4 テーブルが基本形。今回は PayPay 決済を題材に、この 4 つがどう設計され、どうつながるかを読み解いていく。
01 · User
誰が買ったか。アプリにログインするユーザーの情報を管理する。
02 · Company
どこが売ったか。商品を販売する企業・店舗の情報を管理する。
03 · Product
何が売れたか。商品の名称・価格・在庫を管理する。
04 · Purchase
いつ・いくらで。購入の記録を持ち、全テーブルを結ぶ。
読み進める前に:基本用語
データベースはテーブル(表)の集まりでできている。横 1 行分のデータがレコード、縦の項目がカラム。そしてテーブル同士をつなぐ鍵になるのが、次の 2 種類の「キー」だ。
| 用語 | 意味 |
| 主キー (PK) | レコードを一意に区別するための、重複しない番号。例:user_id |
| 外部キー (FK) | 他のテーブルの主キーを参照して、テーブル同士をつなぐ項目。 |
Point
PayPay の決済 API は、Purchase(購入日時)テーブルに paypay_transaction_id という決済 ID を保存することでデータベースと紐づく。外部サービスとの連携も、結局は「テーブルに ID を 1 つ持つ」ことから始まる。
CH 02User — ユーザー管理テーブル
「誰が」を管理する中核テーブル。アプリにログインする全ユーザーの基本情報を保持し、決済時には本人の PayPay アカウントと紐づける。
設計のポイント
- user_id は他テーブルから参照される主キー。ここが全ての起点になる
- PayPay アカウント情報は paypay_account_id として別カラムで保持する
- 個人情報を扱うため、パスワードの hash 化・暗号化が必須
スキーマ:users
| カラム | 型 | 説明 |
| PKuser_id | BIGINT | ユーザーを一意に識別する主キー |
| name | VARCHAR(50) | ユーザー名 |
| email | VARCHAR(255) | メールアドレス(ログインに使用) |
| password_hash | VARCHAR(255) | hash 化したパスワード |
| phone | VARCHAR(20) | 電話番号 |
| paypay_account_id | VARCHAR(64) | 本人の PayPay アカウントとの紐づけ |
| created_at | DATETIME | 登録日時 |
Security
パスワードは平文では絶対に保存しない。カラム名が password ではなく password_hash になっているのは、「hash 化した値しか入れない」という設計意図の表明でもある。
CH 03Company — 会社・店舗管理テーブル
「どこが」を管理するテーブル。商品を販売する企業や店舗の情報を保持し、PayPay 加盟店 ID によって決済時に売上の振込先を特定する。
設計のポイント
- Product テーブルから参照される側になる(1 店舗 : N 商品 の関係)
- paypay_merchant_id で「どの加盟店への支払いか」を識別する
- 1 つの店舗は複数の商品を持てる。商品側に店舗情報を書き写さないのがコツ
スキーマ:companies
| カラム | 型 | 説明 |
| PKcompany_id | BIGINT | 店舗を一意に識別する主キー |
| name | VARCHAR(100) | 企業・店舗名 |
| paypay_merchant_id | VARCHAR(64) | PayPay 加盟店 ID(売上の振込先を特定) |
| address | VARCHAR(255) | 所在地 |
| contact_email | VARCHAR(255) | 連絡先メールアドレス |
| phone | VARCHAR(20) | 電話番号 |
| created_at | DATETIME | 登録日時 |
Point
User テーブルの paypay_account_id が「払う側」の紐づけなら、Company テーブルの paypay_merchant_id は「受け取る側」の紐づけ。決済には必ず両方の ID が関わっている。
CH 04Product — 商品管理テーブル
「何を」を管理するテーブル。販売する商品の名称・価格・在庫を保持し、どの会社が販売しているかを外部キーで紐づける。
設計のポイント
- company_id で店舗テーブルを参照する。ここで初めて外部キー (FK) が登場
- price は決済金額の根拠になる値。型は誤差の出ない DECIMAL を使う
- 在庫(stock)は規模が大きくなったら別テーブルに切り出すことも検討する
スキーマ:products
| カラム | 型 | 説明 |
| PKproduct_id | BIGINT | 商品を一意に識別する主キー |
| FKcompany_id | BIGINT | → companies。販売元の店舗を参照 |
| name | VARCHAR(100) | 商品名 |
| price | DECIMAL(10,2) | 価格。決済金額の根拠になる |
| stock | INT | 在庫数 |
| description | TEXT | 商品説明 |
| created_at | DATETIME | 登録日時 |
Point
商品側に店舗名や住所をコピーせず、company_id という 1 つの外部キーで参照する。店舗情報が変わっても Company テーブルを 1 か所直すだけで済む——これが「関係で持つ」設計の強さ。
CH 05Purchase — 購入日時テーブル × PayPay
「いつ・いくらで」を記録し、4 テーブルすべてを結ぶハブ。User・Company・Product への外部キーを 3 本持ち、さらに PayPay の決済 ID がここに保存されることで、決済システムとデータベースが連動する。
スキーマ:purchases
| カラム | 型 | 説明 |
| PKpurchase_id | BIGINT | 購入レコードを一意に識別する主キー |
| FKuser_id | BIGINT | → users。誰が買ったか |
| FKproduct_id | BIGINT | → products。何を買ったか |
| FKcompany_id | BIGINT | → companies。どこから買ったか |
| amount | DECIMAL(10,2) | 決済金額 |
| paypay_transaction_id | VARCHAR(64) | PayPay API が返す決済 ID |
| payment_status | ENUM | 決済状態(成功・失敗・返金など) |
| purchased_at | DATETIME | 購入日時 |
PayPay 連携
決済が完了すると PayPay API が transaction_id を返し、それを paypay_transaction_id カラムに保存する。以降の返金・照合・問い合わせはすべてこの ID を起点に行われる。外部 API との連携は「相手が発行した ID をこちらのテーブルに持つ」——このシンプルな形が基本。
CH 06まとめ — テーブル同士のつながり
全体を俯瞰すると、Purchase テーブルが 3 つのマスタ(User / Company / Product)をつなぐハブになっていることがわかる。各マスタから見ると Purchase とは 1 : N の関係——1 人のユーザーは何度も買い、1 つの店舗・商品は何度も売れるからだ。
User
users
誰が買ったか
Company
companies
どこが売ったか
Product
products
何が売れたか
1 : N ↓
Purchase
purchases
HUB — 全テーブルをつなぐ
↕ paypay_transaction_id
PayPay
外部 API — 決済 ID を発行して連動
「関係」は SQL でこう辿れる
テーブルを分けて ID でつないでおくと、決済 ID ひとつから「誰が・どこで・何を・いくらで」買ったかを 1 本のクエリで辿れる。
-- 決済IDから購入の全体像を辿る
SELECT u.name, c.name AS shop, p.name AS product, pu.amount, pu.purchased_at
FROM purchases pu
JOIN users u ON pu.user_id = u.user_id
JOIN products p ON pu.product_id = p.product_id
JOIN companies c ON pu.company_id = c.company_id
WHERE pu.paypay_transaction_id = 'pp_8f3a1c…';
学んだこと
決済データの設計は、役割ごとにテーブルを分け、主キーと外部キーでつなぐことに尽きる。Purchase をハブに 3 つのマスタが 1 : N で結ばれ、PayPay のような外部 API も transaction_id を 1 カラム持つだけで連動できる。複雑に見える決済システムも、分解すれば 4 枚の表と数本のキーでできている。
Qiita 原文(データベース入門)を読む