|
| 1 | +--- |
| 2 | +layout: "post" |
| 3 | +title: "[SQL] CTE 사용법 알아보기" |
| 4 | +description: |
| 5 | + "CTE(Common Table Expression)는 SQL에서 복잡한 쿼리를 구조적으로 나누어 가독성과 재사용성을 높여주는 기능으로, \ |
| 6 | + Oracle, SQL Server, PostgreSQL, MySQL 등 주요 데이터베이스에서 지원됩니다. \ |
| 7 | + CTE는 WITH 절을 통해 정의되며, 파생 테이블과 유사하게 사용할 수 있습니다. \ |
| 8 | + 재귀 CTE를 활용하면 계층 구조 데이터 처리와 반복적인 계산을 간결하게 해결할 수 있습니다." |
| 9 | +categories: |
| 10 | + - "스터디-데이터베이스" |
| 11 | +tags: |
| 12 | + - "DATABASE" |
| 13 | + - "SQL" |
| 14 | + - "CTE" |
| 15 | + - "Common Table Expression" |
| 16 | + - "Derived Table" |
| 17 | + - "Subquery" |
| 18 | + - "Recursive CTE" |
| 19 | + - "Hierarchical Query" |
| 20 | + - "Anchor Query" |
| 21 | + - "JPA (ORM) 개발자를 위한 고성능 SQL" |
| 22 | +date: "2025-07-27 11:00:00 +0000" |
| 23 | +toc: true |
| 24 | +image: |
| 25 | + path: "/assets/thumbnails/2025-07-27-cte.jpg" |
| 26 | +--- |
| 27 | + |
| 28 | +# CTE 사용법 알아보기 |
| 29 | + |
| 30 | +**CTE** 는 **Common Table Expression** 의 약자이다. 한국말로는 **공통 테이블 표현식** 이라 한다. |
| 31 | + |
| 32 | +CTE는 WITH절을 통해 정의하며, Oracle, SQL Server, PostgreSQL, MySQL, MariaDB 등 거의 모든 주요 데이터베이스에서 지원된다. (문법은 조금씩 차이가 있다.) |
| 33 | +MySQL 에서는 8.0 버전 부터 지원하고 있다. |
| 34 | + |
| 35 | +## CTE |
| 36 | + |
| 37 | +CTE의 장점은 가독성이 좋다는 점과, 여러 쿼리에서 동일한 CTE를 참조할 수 있다는 것이다. |
| 38 | + |
| 39 | +대부분의 경우, CTE는 파생 테이블(Derived Table / Subquery)로 대체할 수 있으며 그 반대도 가능하다. 이전에 작성한 파생 테이블 쿼리도 동일하게 CTE 형식으로 나타낼 수 있으며, 대부분의 경우에도 성능이 동일하다. |
| 40 | + |
| 41 | +> \[Note] 누군가 어떤 조언을 해주더라도 반드시 현재 사용 중인 데이터베이스에서 정확히 확인해야 한다. 최적화는 가정에 기반한다. 따라서, 쿼리가 잘 작동하는지 여부를 증명하는 유일한 방법은 다시 실행 계획을 확인하는 것이다. |
| 42 | +
|
| 43 | +CTE를 사용하여 하나의 쿼리에서 여러 CTE를 선언할 수 있고, 이전에 정의한 CTE를 또 다른 CTE에서 참조하여 사용할 수 있다. |
| 44 | + |
| 45 | +CTE에서 정의된 쿼리들은 현재 SQL 실행 문(statement)의 범위에서만 유효하며, 메모리 내에서 일시적으로만 존재한다. |
| 46 | + |
| 47 | +### 같은 쿼리를 Derived Table 와 CTE 로 비교 |
| 48 | + |
| 49 | +댓글이 가장 많은 상위 세 개의 게시물의 제목 조회하는 SQL 예시이다. |
| 50 | + |
| 51 | +#### Derived Table |
| 52 | + |
| 53 | +```sql |
| 54 | +SELECT |
| 55 | + p.title AS post_title, |
| 56 | + pc.comment_count AS comment_count |
| 57 | +FROM post p |
| 58 | +JOIN ( |
| 59 | + SELECT |
| 60 | + post_id, |
| 61 | + COUNT(*) as comment_count |
| 62 | + FROM post_comment |
| 63 | + GROUP BY post_id |
| 64 | + ORDER BY comment_count DESC |
| 65 | + LIMIT 3 |
| 66 | +) pc ON p.id = pc.post_id |
| 67 | +ORDER BY comment_count DESC |
| 68 | +``` |
| 69 | + |
| 70 | +#### CTE |
| 71 | + |
| 72 | +```sql |
| 73 | +WITH TopCommentedPosts AS ( |
| 74 | + SELECT |
| 75 | + post_id, |
| 76 | + COUNT(*) AS comment_count |
| 77 | + FROM post_comment |
| 78 | + GROUP BY post_id |
| 79 | + ORDER BY comment_count DESC |
| 80 | + LIMIT 3 |
| 81 | +) |
| 82 | +SELECT |
| 83 | + p.title AS post_title, |
| 84 | + tcp.comment_count AS comment_count |
| 85 | +FROM post p |
| 86 | +JOIN TopCommentedPosts tcp ON p.id = tcp.post_id |
| 87 | +ORDER BY tcp.comment_count DESC; |
| 88 | +``` |
| 89 | + |
| 90 | +## Recursive CTE (재귀적 CTE) |
| 91 | + |
| 92 | +Recursive CTE 를 사용하면 이론적으로 모든 계산 가능한 문제를 해결할 수 있다. (튜링 완전) |
| 93 | + |
| 94 | +절차적 언어(Java 등)에서 반복문을 이용하는 것과 유사하며, 이를 통해 일반적인 데이터 처리와 계층 구조 데이터를 쉽게 해결할 수 있다. |
| 95 | + |
| 96 | +### 예시: 연속된 숫자의 합 구하기 |
| 97 | + |
| 98 | +```sql |
| 99 | +WITH RECURSIVE |
| 100 | + consecutive_number_sum (i, consecutive_sum) |
| 101 | +AS ( |
| 102 | + SELECT 0, 0 |
| 103 | + UNION ALL |
| 104 | + SELECT i + 1, (i + 1) + consecutive_sum |
| 105 | + FROM consecutive_number_sum |
| 106 | + WHERE i < :n |
| 107 | +) |
| 108 | +``` |
| 109 | + |
| 110 | +`n` 이 5일 때 다음과 같은 결과가 나온다. |
| 111 | + |
| 112 | +| i | consecutive_sum | |
| 113 | +| --- | --------------- | |
| 114 | +| 0 | 0 | |
| 115 | +| 1 | 1 | |
| 116 | +| 2 | 3 | |
| 117 | +| 3 | 6 | |
| 118 | +| 4 | 10 | |
| 119 | +| 5 | 15 | |
| 120 | + |
| 121 | +#### 구조 이해하기 |
| 122 | + |
| 123 | +- **Anchor Query**: 첫번째 SELECT 문을 의미. 재귀의 시작값(예: i = 0, sum = 0)을 정의. |
| 124 | +- **UNION ALL**: 각 단계마다 새로운 레코드를 결과 집합에 추가 |
| 125 | +- **recursive member**: 두번째 SELECT 문을 의미한다. 앞선 결과(i, sum 값)를 기반으로 값을 누적하며 반복 실행(i가 5에 도달할 때까지 등) |
| 126 | + - 재귀 멤버의 WHERE 절로 반복 범위를 제어할 수 있다. |
| 127 | + |
| 128 | +### 예시: Hierarchical Query - 계층구조 데이터 조회 |
| 129 | + |
| 130 | +댓글처럼 트리 형태로 계층이 구성될 때 (ex. 자식 댓글이 부모 댓글을 참조, 대댓글) CTE를 사용하여 데이터를 조회할 수 있다. |
| 131 | + |
| 132 | +```sql |
| 133 | +WITH RECURSIVE comment_tree( |
| 134 | + id, root_id, post_id, parent_id, review, created_on) |
| 135 | +AS ( |
| 136 | + SELECT |
| 137 | + id, id, post_id, parent_id, review, created_on |
| 138 | + FROM post_comment |
| 139 | + WHERE post_id = :postId AND parent_id IS NULL |
| 140 | + UNION ALL |
| 141 | + SELECT pc.id, ct.root_id, pc.post_id, pc.parent_id, |
| 142 | + pc.review, pc.created_on |
| 143 | + FROM post_comment pc |
| 144 | + INNER JOIN comment_tree ct ON pc.parent_id = ct.id |
| 145 | +) |
| 146 | +SELECT id, parent_id, root_id, review, created_on |
| 147 | +FROM comment_tree |
| 148 | +``` |
| 149 | + |
| 150 | +## 마무리 |
| 151 | + |
| 152 | +CTE(Common Table Expression)는 복잡한 SQL 쿼리를 구조적으로 나누어 가독성과 재사용성을 높여주는 유용한 기능입니다. 일반적인 서브쿼리와 유사하게 사용할 수 있을 뿐만 아니라, 재귀 CTE를 활용하면 계층 구조나 반복적인 계산과 같은 복잡한 데이터 처리도 SQL만으로 간결하게 해결할 수 있습니다. |
0 commit comments