SQL 서브쿼리(subquery)는 다른 쿼리 내부에 포함된 쿼리를 의미합니다. 주로 SQL 문의 WHERE 절이나 HAVING 절에서 사용되며, 하나의 쿼리 결과를 다른 쿼리의 인자로 지정할 수 있게 해주는 강력한 기능입니다.
SQL 쿼리를 작성하다 보면 다른 쿼리의 실행 결과를 기반으로 조건을 지정해야 하는 경우가 자주 발생합니다. 예를 들어 고객 목록이 있고, 특정 금액 이상의 주문을 한 고객만 조회하고 싶다고 가정해 봅시다. 그런데 주문 정보와 고객 정보는 서로 다른 테이블에 나누어 저장되어 있어 단순한 조회만으로는 원하는 결과를 얻기 어렵습니다.
바로 이럴 때 SQL 서브쿼리가 유용합니다. 서브쿼리는 '중첩 쿼리(nested query)'라고도 불리며, 다른 SQL 쿼리의 WHERE 절 안에 위치한 SELECT 문입니다. 메인 쿼리는 서브쿼리가 반환한 결과를 활용해 최종 결과를 도출합니다.
이 글에서는 몇 가지 실전 예제를 통해 SQL에서 서브쿼리를 사용하는 방법을 자세히 알아보겠습니다.
SQL 서브쿼리 기본 개념
SQL 서브쿼리는 말 그대로 쿼리 안에 포함된 또 다른 쿼리입니다. 다른 쿼리의 결과에 의존하는 쿼리를 실행할 때 사용하며, 두 개의 별도 쿼리를 작성한 뒤 결과를 일일이 복사·붙여넣을 필요 없이 한 번에 처리할 수 있다는 장점이 있습니다. 서브쿼리는 WHERE 절 또는 HAVING 절에 위치합니다.
SQL SELECT 문에서 서브쿼리를 사용하는 기본 문법은 다음과 같습니다:
SELECT name FROM products WHERE supplier_id IN (SELECT id FROM suppliers WHERE local = True);
위 쿼리는 국내(local) 공급업체가 공급하는 모든 제품의 이름을 조회합니다. 여기서는 서브쿼리를 SQL IN 연산자의 입력값으로 사용했습니다. IN 연산자는 products 테이블에서 supplier_id가 서브쿼리 결과에 포함된 모든 레코드를 반환합니다.
서브쿼리는 SELECT 문뿐만 아니라 INSERT, UPDATE, DELETE 문에서도 활용할 수 있습니다.
서브쿼리는 반드시 괄호( ) 안에 작성해야 합니다. 괄호는 서브쿼리의 시작과 끝을 명확하게 구분해 주는 역할을 합니다.
또한 서브쿼리에서 필요한 데이터를 정확히 선택하는 것이 매우 중요합니다. 위 예제에서는 suppliers 테이블에서 id 컬럼을 선택했습니다. 만약 다른 컬럼을 선택했다면 메인 쿼리가 supplier ID에 의존하고 있기 때문에 쿼리가 정상적으로 동작하지 않습니다.
SQL 서브쿼리 예제: SELECT
예를 들어 성적 우수 명단(honor roll)에 오른 학생 목록을 조회하고 싶다면 서브쿼리를 활용할 수 있습니다. 이때 성적 우수 명단 정보는 별도의 테이블에 저장되어 있다고 가정합니다.
다음 예제를 통해 SQL 서브쿼리의 동작 방식을 살펴보겠습니다. 아래 쿼리는 200달러 이상의 주문을 한 적이 있는 모든 고객 목록을 반환합니다:
SELECT * FROM customers WHERE id IN (SELECT DISTINCT customer_id FROM orders WHERE cost > 200);
이 쿼리의 실행 결과는 다음과 같습니다:
| name | address | loyalty_plan | id | |
| Katy | katy.l@gmail.com | Mountain View, CA | None | 4 |
| John | john.p@outlook.com | Boston, MA | None | 1 |
(2 rows)
첫 번째 줄에서 customers 테이블의 모든 컬럼을 선택합니다. 그다음, 고객 ID가 서브쿼리 결과에 포함된 고객만 조회하도록 조건을 지정했습니다. 서브쿼리는 orders 테이블에서 주문 금액(cost)이 200달러를 초과하는 레코드의 고유한 customer_id 값을 추출합니다. 이 조건에 해당하는 고객들이 최종 결과에 포함됩니다.
SQL 서브쿼리: INSERT
이번에는 200달러 이상 구매한 고객의 모든 레코드를 high_value_customers라는 테이블에 삽입해 보겠습니다. SQL 서브쿼리와 INSERT 명령을 함께 사용하면 간단히 처리할 수 있습니다:
INSERT INTO high_value_customers SELECT * FROM customers WHERE id IN (SELECT DISTINCT customer_id FROM orders WHERE cost > 200);
high_value_customers 테이블을 조회하면 고액 결제 고객 전체를 한 테이블에서 확인할 수 있습니다:
| name | address | loyalty_plan | id | |
| Katy | katy.l@gmail.com | Mountain View, CA | Gold | 4 |
| John | john.p@outlook.com | Boston, MA | None | 1 |
(2 rows)
SQL 서브쿼리: UPDATE
서브쿼리는 테이블의 데이터를 수정할 때도 활용할 수 있습니다. 예를 들어 고액 결제를 한 모든 고객의 멤버십 등급(loyalty_plan)을 'High Value'로 변경하고 싶다면 다음과 같은 쿼리를 사용할 수 있습니다:
UPDATE customers SET loyalty_plan = 'High Value' WHERE id IN (SELECT DISTINCT customer_id FROM orders WHERE cost > 200);
customers 테이블을 다시 조회하면 두 고액 결제 고객의 멤버십 등급이 정상적으로 업데이트된 것을 확인할 수 있습니다:
| name | address | loyalty_plan | id | |
| Katy | katy.l@gmail.com | Mountain View, CA | High Value | 4 |
| John | john.p@outlook.com | Boston, MA | High Value | 1 |
(2 rows)
SQL 서브쿼리: DELETE
SQL DELETE 문과 함께 서브쿼리를 사용하면 테이블에서 특정 행 또는 여러 행을 한 번에 삭제할 수 있습니다.
앞서 새 테이블로 옮겨둔 고액 결제 고객들의 주문 내역을 삭제하는 명령을 작성해 보겠습니다. 이 작업 역시 서브쿼리에 의존해야 합니다:
DELETE FROM orders WHERE customer_id IN (SELECT id FROM customers WHERE loyalty_plan = 'High Value');
실행 결과, 고액 결제 고객들의 모든 주문이 삭제되었습니다. 중첩 서브쿼리가 포함된 쿼리 실행 후 남은 데이터는 다음과 같습니다:
| id | item_name | cost | customer_id |
| 5 | Oak Chair | 100 | 3 |
(1 row)
마무리
SQL 서브쿼리는 쿼리 안에 포함된 또 다른 쿼리로, 하나의 쿼리 결과를 다른 쿼리에서 활용할 수 있게 해줍니다. 서브쿼리는 SQL 문의 WHERE 절 또는 HAVING 절에 지정합니다.
서브쿼리를 사용하면 다른 쿼리의 결과에 의존하는 쿼리를 손쉽게 실행할 수 있습니다. 이 가이드에서는 SQL 서버에서 서브쿼리를 작성하는 방법을 살펴보았으며, SELECT, INSERT, UPDATE, DELETE 문에서 서브쿼리를 활용하는 방법도 함께 알아보았습니다.
SQL을 더 깊이 학습하고 싶다면 체계적으로 정리된 SQL 학습 가이드와 검증된 추천 강좌 목록을 참고해 학습 여정을 이어가 보세요. 꾸준한 실습과 예제 학습이 실력 향상의 지름길입니다.