MariaDB講座 / INNER JOINで関連する行を読み取る

INNER JOINは、結合条件が真になる行の組を返します。ONに関連キーを明示し、結果の一行が顧客・注文・明細のどれかを決めてから、繰り返し表示や件数を解釈します。

関連条件と出力の単位を決める

注文のcustomer_idと顧客の主キーを対応させます。同じ列名だけでは正しい関連は作られません。orders AS o、customers AS cのような短い別名と修飾付きの列を使うと所有元が分かります。

ON c.customer_id=o.customer_idは関連、WHERE o.status='paid'は必要な注文状態です。設計の根拠はキーと表の関係、不一致でも残す場合はLEFT JOINで確認します。

四表の独立したデータを作る

クライアントの接続手順でcourse_userとしてshopcourseへ接続し、SELECT VERSION(), DATABASE();とSHOW TABLES;を確認します。以下には必要な四表の作成とデータがすべてあり、以前の記事の表を必要としません。UTF-8の.sqlファイルで実行し、同名の表があれば定義と内容を調べてから判断します。

USE shopcourse;
CREATE TABLE c26_customers (
 customer_id INT NOT NULL PRIMARY KEY,
 customer_name VARCHAR(50) NOT NULL,
 city VARCHAR(30) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE c26_products (
 product_id INT NOT NULL PRIMARY KEY,
 product_name VARCHAR(100) NOT NULL,
 category VARCHAR(30) NOT NULL,
 price DECIMAL(10,2) NOT NULL CHECK (price >= 0),
 stock INT NOT NULL CHECK (stock >= 0)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE c26_orders (
 order_id INT NOT NULL PRIMARY KEY,
 customer_id INT NOT NULL,
 ordered_at DATETIME NOT NULL,
 status VARCHAR(20) NOT NULL,
 shipping_fee DECIMAL(10,2) NOT NULL DEFAULT 1000,
 CONSTRAINT fk_c26_order_customer FOREIGN KEY (customer_id)
  REFERENCES c26_customers(customer_id) ON DELETE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE c26_items (
 order_id INT NOT NULL,
 product_id INT NOT NULL,
 quantity INT NOT NULL CHECK (quantity > 0),
 unit_price DECIMAL(10,2) NOT NULL CHECK (unit_price >= 0),
 PRIMARY KEY (order_id, product_id),
 CONSTRAINT fk_c26_item_order FOREIGN KEY (order_id)
  REFERENCES c26_orders(order_id) ON DELETE RESTRICT,
 CONSTRAINT fk_c26_item_product FOREIGN KEY (product_id)
  REFERENCES c26_products(product_id) ON DELETE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO c26_customers VALUES
 (1,'김민수','서울'),(2,'이서연','부산'),
 (3,'박지우','서울'),(4,'최하늘','대전');
INSERT INTO c26_products VALUES
 (1,'노트','문구',3500,20),(2,'볼펜','문구',1500,50),
 (3,'머그컵','생활',8000,12),(4,'스탠드','생활',25000,5);
INSERT INTO c26_orders
 (order_id,customer_id,ordered_at,status,shipping_fee) VALUES
 (101,1,'2026-01-10 10:00:00','paid',1000),
 (102,1,'2026-01-20 11:00:00','paid',1000),
 (103,2,'2026-02-05 12:00:00','paid',1000),
 (104,3,'2026-02-10 13:00:00','canceled',1000);
INSERT INTO c26_items VALUES
 (101,1,2,3500),(101,2,3,1500),(102,3,1,8000),
 (103,1,1,3500),(103,3,2,8000),(104,2,1,1500);

顧客名・都市・商品名は原文の韓国語サンプルです。商品1~4はノート、ペン、マグカップ、ライト、顧客1・3はソウル、2は釜山、4は大田です。文字列を維持して実行し、説明とは安定したIDで照合できます。

主に顧客と注文を使いますが、共通の注文モデルとして商品と明細も含みます。後片付けもこの記事のc26_表だけが対象です。

注文へ顧客を結合する

注文単位の一覧です。

SELECT o.order_id,c.customer_id,c.customer_name,o.status
FROM c26_orders AS o
INNER JOIN c26_customers AS c ON c.customer_id=o.customer_id
ORDER BY o.order_id;

注文101・102は顧客1、103は2、104は3です。状態は前の三件がpaid、104がcanceledです。顧客1の名前が二度出るのは二つの注文があるからで、誤った重複とは限りません。一顧客一行が目的ならEXISTSや意図した集計を選び、DISTINCTで意味のある注文を隠さないようにします。

関連と業務条件を分けて読む

支払い済みだけを表示します。

SELECT o.order_id,c.customer_name,o.ordered_at
FROM c26_orders AS o
INNER JOIN c26_customers AS c ON c.customer_id=o.customer_id
WHERE o.status='paid' ORDER BY o.order_id;

注文101・102・103です。INNER JOINでは一部の条件を移して結果が同じになる場合がありますが、外部結合では違うことがあるため、役割を明確にします。確認中は両方の関連キーも表示し、変更可能な名前や無関係な同名IDで結ばないでください。

すべての組み合わせと比較する

条件なしの積を数えます。

SELECT COUNT(*) AS unconstrained_pairs
FROM c26_customers AS c CROSS JOIN c26_orders AS o;

顧客4×注文4で16組に対し、正しい関連結合は4行です。CROSS JOINは全組み合わせが必要な場合に有効ですが、偶然に使うと無関係な顧客が注文に割り当てられます。この例では外部キーが親を保証しますが、一般に不一致行はINNER JOINから落ちます。

件数と列の曖昧さを調べる

Column is ambiguousなら共有列をo.やc.で修飾します。多すぎる場合はキーの一意性、関連の多重度と抜けた条件、少なければ絞り込みと不一致を調べます。必要な順はORDER BYで指定します。

再実習はDB名、実際の定義と保存行を確認し、必要なデータを保存してから行います。この記事の削除してよい四表だけを、参照する子から親の順に削除します。DROP TABLEは定義とデータを消し、通常のROLLBACKでは戻せません。

DROP TABLE shopcourse.c26_items;
DROP TABLE shopcourse.c26_orders;
DROP TABLE shopcourse.c26_products;
DROP TABLE shopcourse.c26_customers;

関連する公式リファレンス