> ## Documentation Index
> Fetch the complete documentation index at: https://villagesql.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# MySQL の INNER JOIN

> MySQL の INNER JOIN 句が ON と USING を使って 2 つ以上のテーブルの行を一致させる仕組み、そして 1 行が多数の行に一致すると行数が増える理由。

> [card] [VillageSQL is a drop-in replacement for MySQL with extensions.](https://villagesql.com/docs/en/8.4/stable/install-server.md)
>
> All examples on this page work on VillageSQL. Install Now →

`INNER JOIN` は一致が成立した行だけを残します。あるテーブルの行が別の
テーブルの行を指していて、その両方が欲しいときに使う結合です。

始める前に `mysql -u root -p sakila` でクライアントを開いてください。

## INNER JOIN の仕組み

```sql theme={null}
SELECT COLUMN_LIST
FROM LEFT_TABLE
INNER JOIN RIGHT_TABLE ON LEFT_TABLE.COLUMN_NAME = RIGHT_TABLE.COLUMN_NAME;
```

`COLUMN_LIST` を欲しい列に、`LEFT_TABLE` を起点にするテーブルに、
`RIGHT_TABLE` を追加の列を取り出すテーブルに、各 `COLUMN_NAME` をそれぞれの
側で両者をつなぐ列に置き換えます。

`ON` 句はどの行同士が対応するかを指定します。MySQL は左テーブルの各行を
取り、その条件が成り立つ右テーブルの行を見つけ、一致ごとに 1 つの結合された
行を返します。何も見つからなかった行は、どちら側でも除外されます。
[LEFT JOIN](https://villagesql.com/docs/ja/tutorial/left-join.md) は、そうした行を代わりに残す句です。

## 例

### 2 つのテーブルを結合する

`film.language_id` は `language.language_id` を指しています。この組を
一致させれば、タイトルの横に言語名を表示できます。

```sql theme={null}
SELECT f.title, l.name
FROM film AS f
INNER JOIN language AS l ON f.language_id = l.language_id
ORDER BY f.title;
```

```text theme={null}
+-----------------------------+---------+
| title                       | name    |
+-----------------------------+---------+
| ACADEMY DINOSAUR            | English |
| ACE GOLDFINGER              | English |
| ADAPTATION HOLES            | English |
...
+-----------------------------+---------+
1000 rows in set (0.00 sec)
```

1,000 行で、`film` 単体と同じ件数です。すべての映画がちょうど 1 つの言語に
一致しました。結合列が単一の行を指しているときはこうなります。

### 外部キーをたどって結合する

顧客の住所は別のテーブルに保存されています。

```sql theme={null}
SELECT c.first_name, c.last_name, a.address
FROM customer AS c
INNER JOIN address AS a ON c.address_id = a.address_id
ORDER BY c.last_name, c.first_name;
```

```text theme={null}
+-------------+--------------+----------------------------------------+
| first_name  | last_name    | address                                |
+-------------+--------------+----------------------------------------+
| RAFAEL      | ABNEY        | 48 Maracaíbo Place                     |
| NATHANIEL   | ADAM         | 786 Matsue Way                         |
| KATHLEEN    | ADAMS        | 334 Munger (Monghyr) Lane              |
...
+-------------+--------------+----------------------------------------+
599 rows in set (0.00 sec)
```

599 行、顧客 1 人につき 1 行です。

### 3 つのテーブルを結合する

テーブルを増やすごとに `INNER JOIN` 句を 1 つ追加します。Sakila は所在地を
`address`、`city`、`country` に分けているので、都市名にたどり着くには
2 段階必要です。

```sql theme={null}
SELECT c.first_name, c.last_name, ci.city
FROM customer AS c
INNER JOIN address AS a ON c.address_id = a.address_id
INNER JOIN city AS ci ON a.city_id = ci.city_id
ORDER BY c.last_name, c.first_name;
```

```text theme={null}
+-------------+--------------+----------------------------+
| first_name  | last_name    | city                       |
+-------------+--------------+----------------------------+
| RAFAEL      | ABNEY        | Talavera                   |
| NATHANIEL   | ADAM         | Joliet                     |
| KATHLEEN    | ADAMS        | Arak                       |
...
+-------------+--------------+----------------------------+
599 rows in set (0.00 sec)
```

依然として 599 行です。各結合が単一の行に一致したので、件数は動きませんでした。

### USING で短く書く

2 つの列が同じ名前のとき、`USING` が `ON` の代わりになり、名前を 1 回だけ
書きます。

```sql theme={null}
SELECT f.title, l.name
FROM film AS f
INNER JOIN language AS l USING (language_id)
ORDER BY f.title;
```

```text theme={null}
+-----------------------------+---------+
| title                       | name    |
+-----------------------------+---------+
| ACADEMY DINOSAUR            | English |
| ACE GOLDFINGER              | English |
| ADAPTATION HOLES            | English |
...
+-----------------------------+---------+
1000 rows in set (0.00 sec)
```

例 1 と同じ 1,000 行です。`USING` は両側で名前が一致するときにしか使えない
ので、常に使えるのは `ON` の形です。

## 複数行への一致は行を増やす

結合は一致ごとに 1 行を返すので、多数の行に一致する行は何度も返ってきます。
各映画には複数の俳優がいます。

```sql theme={null}
SELECT f.title, fa.actor_id
FROM film AS f
INNER JOIN film_actor AS fa ON f.film_id = fa.film_id
ORDER BY f.title, fa.actor_id;
```

```text theme={null}
+-----------------------------+----------+
| title                       | actor_id |
+-----------------------------+----------+
| ACADEMY DINOSAUR            |        1 |
| ACADEMY DINOSAUR            |       10 |
| ACADEMY DINOSAUR            |       20 |
...
+-----------------------------+----------+
5462 rows in set (0.00 sec)
```

1,000 本の映画から 5,462 行、各タイトルが俳優ごとに 1 回繰り返されています。
結合が予想より多くの行を返す通常の理由はこれであり、結合に対して計算した
合計が大きくなりすぎる理由でもあります。

## 人がつまずく 2 つのこと

**一致しなかった行は空欄ではなく消える。** `film` ではなく `language` を
起点にしても、1,005 行ではなく 1,000 行が返ります。Sakila の他の 5 言語は
どの映画にも一致せず、脱落します。内部結合は一致したものを報告し、欠けて
いるものは決して報告しません。[LEFT JOIN](https://villagesql.com/docs/ja/tutorial/left-join.md) が、もう
一方の問いを立てる方法です。

**`ON` の欠落はエラーにならない。** MySQL は `ON` のない `INNER JOIN` を
両テーブルのすべての組み合わせとして読むので、入力ミスはエラーではなく
1,000 × 6 行を返します。[CROSS JOIN](https://villagesql.com/docs/ja/tutorial/cross-join.md) では、意図的な
場合と偶発的な場合の両方を扱います。

## まとめ

* 一致するテーブルの行を組み合わせるには、`INNER JOIN` を `ON` と共に使います。
* テーブルを増やすごとに `INNER JOIN` 句を 1 つ追加します。
* 一致しない行は消えます。内部結合は一致したものだけを報告するからです。
* 1 行が多数の行に一致すると、行数が増えます。
* `USING` は、列が両側で同じ名前のときにだけ使います。

## 関連項目

* [結合の仕組み](https://villagesql.com/docs/ja/tutorial/joins.md) — この結合と他の種類との違い
* [LEFT JOIN](https://villagesql.com/docs/ja/tutorial/left-join.md) — この結合が除外する行を残す
* [MySQL の JOIN のパフォーマンスとよくある間違い](https://villagesql.com/docs/ja/guides/joins.md) — 結合へのインデックス付けと、誤った行数が出る場合
* [MySQL の外部キー](https://villagesql.com/docs/ja/guides/foreign-keys.md) — これらの結合がたどる関係


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.