> ## 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 EXISTS

> MySQL EXISTS 연산자가 서브쿼리가 행을 하나라도 찾는지 검사하는 방법, NOT EXISTS가 일치하지 않는 행을 나열하는 방법, 그리고 NOT IN이 안전하지 않은 곳에서 NOT EXISTS가 안전한 이유입니다.

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

`EXISTS`는 예 또는 아니오의 질문을 합니다: 이 서브쿼리가 무언가를 찾았는가. 서브쿼리가 무엇을 반환했는지는 상관하지 않고 행이 있었는지만 보므로, 관련된 행이 있는지 검사하되 그 열은 하나도 필요 없을 때 알맞은 도구입니다.

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

## EXISTS의 동작 방식

```sql theme={null}
SELECT COLUMN_LIST
FROM TABLE_NAME AS OUTER_ALIAS
WHERE EXISTS (
  SELECT 1 FROM OTHER_TABLE AS INNER_ALIAS
  WHERE INNER_ALIAS.COLUMN_NAME = OUTER_ALIAS.COLUMN_NAME
);
```

`COLUMN_LIST`를 원하는 열로, `TABLE_NAME`과 `OTHER_TABLE`을 2개의 테이블로, `OUTER_ALIAS`와 `INNER_ALIAS`를 그 이름으로, `COLUMN_NAME`을 둘을 연결하는 열로, 안쪽의 `WHERE`를 행을 관련되게 만드는 조건으로 바꾸세요.

`SELECT 1`은 관례입니다. select 목록은 무시되므로 `SELECT 1`, `SELECT *`, `SELECT column`은 똑같이 동작하고, `1`은 아무것도 읽지 않는다는 것을 분명하게 말해 줍니다.

이 서브쿼리는 상관 서브쿼리입니다: 바깥 행을 참조합니다. 서버는 첫 번째로 일치하는 행에서 멈출 수 있는데, 예라고 답하기에는 1개면 충분하기 때문입니다.

## 예제

### 일치하는 것이 있는 행

```sql theme={null}
SELECT l.name
FROM language AS l
WHERE EXISTS (SELECT 1 FROM film AS f WHERE f.language_id = l.language_id)
ORDER BY l.name;
```

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

6개의 언어 중 1개에 영화가 있습니다.

### 일치하는 것이 없는 행

`NOT EXISTS`가 나머지 부분이고, 안티 조인을 쓰는 방법입니다.

```sql theme={null}
SELECT l.name
FROM language AS l
WHERE NOT EXISTS (SELECT 1 FROM film AS f WHERE f.language_id = l.language_id)
ORDER BY l.name;
```

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

나머지 5개입니다. [LEFT JOIN](https://villagesql.com/docs/ko/tutorial/left-join.md) 레슨은 같은 질문에 `WHERE f.film_id IS NULL`로 답하고, 2가지 형태는 같은 행을 반환합니다.

## NOT IN이 안전하지 않은 곳에서 NOT EXISTS는 안전합니다

[IN](https://villagesql.com/docs/ko/tutorial/in.md) 레슨은 목록에 NULL이 들어 있을 때 `NOT IN`이 행을 하나도 반환하지 않는 모습을 보여 줍니다. `NOT EXISTS`에는 그런 함정이 없는데, 값을 비교하지 않기 때문입니다: 행을 셉니다.

그러므로 서브쿼리가 NULL일 수 있는 열을 읽을 때는 `NOT EXISTS`를 쓰세요. `NOT IN`은 `NOT NULL`로 선언된 열에서는 괜찮고, 그것이 더 이상 성립하지 않는 순간 위험 요소가 됩니다.

## EXISTS는 행을 절대 늘리지 않습니다

일치하는 행이 여러 개인 테이블로의 조인은 바깥 행을 일치마다 한 번씩 반복하며, [INNER JOIN](https://villagesql.com/docs/ko/tutorial/inner-join.md) 레슨이 그것을 보여 줍니다. `EXISTS`는 그렇게 할 수 없습니다: 예 또는 아니오로 답하므로, 일치가 몇 개이든 각 바깥 행은 한 번만 나타납니다.

`EXISTS`, `IN`, 조인은 모두 "관련된 행이 있다"를 표현할 수 있고, 어느 것이 가장 빠른지는 데이터에 따라 다릅니다. [MySQL의 서브쿼리 대 JOIN](https://villagesql.com/docs/ko/guides/subqueries-vs-joins.md)이 플랜과 함께 그것들을 비교합니다.

## 요약

* 서브쿼리가 행을 하나라도 찾는지 검사하려면 `EXISTS`를 사용하세요.
* select 목록은 무시되므로, 안에는 `SELECT 1`을 쓰세요.
* 일치하는 것이 없는 행에는 `NOT EXISTS`를 사용하세요.
* 서브쿼리 열이 NULL일 수 있으면 `NOT IN`보다 `NOT EXISTS`를 쓰세요.
* 조인이라면 일으켰을 행의 증가가 없다고 예상하세요.

## 함께 보기

* [상관 서브쿼리](https://villagesql.com/docs/ko/tutorial/correlated-subqueries.md) — EXISTS가 사용하는 서브쿼리 형태
* [IN](https://villagesql.com/docs/ko/tutorial/in.md) — NULL 함정이 있는 연산자
* [LEFT JOIN](https://villagesql.com/docs/ko/tutorial/left-join.md) — 조인으로 묻는 같은 질문
* [MySQL의 서브쿼리 대 JOIN](https://villagesql.com/docs/ko/guides/subqueries-vs-joins.md) — 옵티마이저가 선호하는 형태


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