> ## 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 LEFT JOIN

> MySQL LEFT JOIN이 아무것과도 일치하지 않는 행을 어떻게 남기는지, 그 행을 IS NULL로 어떻게 나열하는지, 그리고 WHERE의 필터가 왜 LEFT JOIN을 다시 내부 조인으로 바꾸는지 설명합니다.

> [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 →

내부 조인은 일치한 것을 보고합니다. `LEFT JOIN`은 일치 여부와 상관없이 왼쪽 테이블의 모든 행을 남기는데, 그것이 반대 질문에 답하는 방법입니다: 무엇이 일치하지 않았는가.

시작하기 전에 `mysql -u root -p sakila`로 클라이언트를 여세요.

## LEFT JOIN의 동작 방식

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

`COLUMN_LIST`를 원하는 열로, `LEFT_TABLE`을 모든 행을 남기고 싶은 테이블로, `RIGHT_TABLE`을 추가 열을 가져올 테이블로, 각 `COLUMN_NAME`을 그쪽에서 둘을 연결하는 열로 바꾸세요.

서버는 일치하는 행에 대해서는 내부 조인처럼 동작합니다. 아무것과도 일치하지 않는 왼쪽 행에 대해서는 그 행을 어쨌든 남기고 오른쪽 테이블의 모든 열을 NULL로 채웁니다. `LEFT OUTER JOIN`은 같은 절을 풀어 쓴 것이고, `OUTER`라는 단어는 아무것도 더하지 않습니다.

## 예제

### 일치하지 않은 행 남기기

Sakila에는 6개의 언어가 있고 모든 영화가 영어이므로, 5개의 언어는 어떤 영화와도 일치하지 않습니다. 내부 조인은 그것들을 버립니다. 이 조인은 버리지 않습니다.

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

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

1,005개의 행: 1,000개의 일치에, 아무것과도 일치하지 않은 5개의 언어마다 1개의 행이 더해진 것입니다.

### 아무것과도 일치하지 않은 행만 나열하기

실제 일치에서는 결코 NULL일 수 없는 오른쪽 테이블의 열에 `WHERE`를 추가하면, 일치하지 않은 행만 정확히 남게 됩니다.

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

```text theme={null}
+----------+-------+
| name     | title |
+----------+-------+
| French   | NULL  |
| German   | NULL  |
| Italian  | NULL  |
| Japanese | NULL  |
| Mandarin | NULL  |
+----------+-------+
5 rows in set (0.00 sec)
```

`f.film_id`는 `film`의 기본 키이므로, 일치한 행에는 언제나 값이 있습니다. 거기의 NULL은 조인이 아무것과도 일치하지 않은 행을 채워 넣은 데서만 올 수 있습니다. 오른쪽 테이블의 어떤 `NOT NULL` 열을 검사해도 같은 논리가 성립합니다. 기본 키가 가장 안전한 선택인데, 모든 테이블에서 `NOT NULL`이기 때문입니다.

### 실무에서 이것이 답하는 질문

카탈로그의 모든 영화가 선반에 있는 것은 아닙니다. `inventory`가 보유 사본을 담고 있으므로, `inventory` 행이 없는 영화는 아무도 빌릴 수 없는 영화입니다.

```sql theme={null}
SELECT f.title
FROM film AS f
LEFT JOIN inventory AS i ON f.film_id = i.film_id
WHERE i.film_id IS NULL
ORDER BY f.title;
```

```text theme={null}
+------------------------+
| title                  |
+------------------------+
| ALICE FANTASIA         |
| APOLLO TEEN            |
| ARGONAUTS TOWN         |
...
+------------------------+
42 rows in set (0.00 sec)
```

1,000개의 영화 중 42개에 사본이 없습니다. `LEFT JOIN`에 `WHERE <right key> IS NULL`을 더한 이 패턴이, 한 테이블에서 다른 테이블에 일치하는 것이 없는 행을 찾는 방법입니다.

## WHERE의 필터는 외부 조인을 무효화합니다

`LEFT JOIN`에서 사람들의 시간을 가장 많이 잡아먹는 실수입니다. 오른쪽 테이블에 보통의 조건을 추가하는 것은 무해해 보입니다.

```sql theme={null}
SELECT l.name, f.title
FROM language AS l
LEFT JOIN film AS f ON l.language_id = f.language_id
WHERE f.title LIKE 'ACADEMY%'
ORDER BY l.name, f.title;
```

```text theme={null}
+---------+------------------+
| name    | title            |
+---------+------------------+
| English | ACADEMY DINOSAUR |
+---------+------------------+
1 row in set (0.00 sec)
```

1개의 행입니다. 일치하지 않은 5개의 언어가 사라졌으므로, 쿼리가 무엇이라고 쓰여 있든 이제 내부 조인입니다. 이유는 순서입니다: 조인이 먼저 실행되어 `f.title`이 NULL인 5개의 행을 만들고, 그다음 `WHERE`가 각 행을 검사하는데, `NULL LIKE 'ACADEMY%'`는 참이 아니라 NULL이므로 `WHERE`가 그 행들을 버립니다.

같은 조건을 `ON`으로 옮기면 일치로 간주되는 것의 일부가 됩니다.

```sql theme={null}
SELECT l.name, f.title
FROM language AS l
LEFT JOIN film AS f ON l.language_id = f.language_id AND f.title LIKE 'ACADEMY%'
ORDER BY l.name, f.title;
```

```text theme={null}
+----------+------------------+
| name     | title            |
+----------+------------------+
| English  | ACADEMY DINOSAUR |
| French   | NULL             |
| German   | NULL             |
| Italian  | NULL             |
| Japanese | NULL             |
| Mandarin | NULL             |
+----------+------------------+
6 rows in set (0.00 sec)
```

6개의 언어 모두이고, 그중 1개만 일치하는 영화를 찾았습니다. 이 규칙은 외워 둘 만합니다: `LEFT JOIN`에서 오른쪽 테이블에 대한 조건은 `ON`에, 왼쪽 테이블에 대한 조건은 `WHERE`에 속합니다. 의도된 예외 하나는 예제 2의 `IS NULL`로, 일치한 행을 버리는 것이 목적 그 자체인 경우입니다.

## 요약

* 왼쪽 테이블의 모든 행을 일치 여부와 상관없이 남기려면 `LEFT JOIN`을 사용하세요.
* 아무것과도 일치하지 않은 행의 오른쪽 열은 모두 NULL이 됩니다.
* 일치하지 않은 행만 나열하려면 오른쪽 테이블의 `NOT NULL` 열을 `IS NULL`로 검사하세요.
* 오른쪽 테이블에 대한 조건은 `ON`에 두세요. `WHERE`에 두면 일치하지 않은 행이 버려집니다.
* `LEFT OUTER JOIN`은 같은 절의 다른 표기로 읽으세요.

## 함께 보기

* [INNER JOIN](https://villagesql.com/docs/ko/tutorial/inner-join.md) — 일치만 남기는 조인
* [RIGHT JOIN](https://villagesql.com/docs/ko/tutorial/right-join.md) — 반대쪽에서 본 같은 발상
* [IS NULL](https://villagesql.com/docs/ko/tutorial/is-null.md) — 일치하지 않은 행을 보이게 하는 검사
* [EXISTS](https://villagesql.com/docs/ko/tutorial/exists.md) — 조인 없이 묻는 같은 질문
* [MySQL JOIN 성능과 흔한 실수](https://villagesql.com/docs/ko/guides/joins.md) — 버그 보고서로 본 같은 함정


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