부속질의 Subquery
는 하나의 SQL 질의문 내에 또 다른 SQL 질의문이 중첩된 질의문을 의미합니다.
부속질의
는 다른 테이블에서부터 가져온 결과 데이터를 사용해서 현재 테이블에 데이터를 검색 및 조작하는 데 사용됩니다.
예시를 위해 다음 두 가지 학생 테이블, 도서 대출 테이블
을 생성했습니다.특정 학생의 이름만을 알 때, 이름을 통해 어떤 도서들을 대출했는지 알고자하는 경우에 두 테이블의 데이터를 동시에 활용해서 알아내야합니다. 이럴 경우 부속 질의를 사용해서 데이터를 탐색할 수 있습니다.
부속질의
는 최종 결과를 반환하는 주질의(외부 질의)
와 주질의에서 사용될 데이터 결과를 넘겨주는 부속질의(내부질의)
로 나뉘어집니다.
#외부 질의
SELECT name, book_title
FROM book_loans_table
WHERE student_id = (
#부속질의
SELECT student_id
FROM students_table
WHERE name = '이수빈'
);
부속질의
는 내포되는 위치에 따라 다시 SELECT 부속질의(스칼라 부속질의), FROM 부속질의(인라인 뷰), WHERE 부속질의(중첩질의)
로 구분됩니다.
일반적인 상황에서 단순히
부속질의
라고 말한다면WHERE
에 사용되는 부속질의를 의미한다고 합니다.
SELECT 내부질의(스칼라 부속질의)
는 SELECT
구문에서 사용되는 부속질의 입니다. 부속질의에 대한 결과를 하나의 행(row) 스칼라 값으로 반환합니다. 반드시 단일 결과를 반환하기 때문에 부속질의 결과가 여러개인 경우에는 에러를 던집니다.
다음은 이름이 '김민수'인 학생의 총 대출 횟수를 출력하는 스칼라 부속질의 예제입니다. 주질의에서는 이름을, 부속질의에서는 대출 횟수를 조회합니다.
SELECT name,
(SELECT COUNT(*)
FROM book_loans_table b
WHERE b.student_id = s.student_id) AS total_loans
FROM students_table s
WHERE name = '김민수';
내부적으로는 다음 순서로 실행됩니다.
1. students_table
에서 모든 레코드를 가져온다.
2. 이름이 '김민수'인 레코드만 필터링한다.
3. 스칼라 부속질의를 수행한다. (총 대출 수 계산)
FROM 부속질의(인라인 뷰)
는 FROM
구문에서 사용되는 부속질의로 부속질의 결과를 가상 테이블의 형태로 사용하기 때문에 인라인 뷰라고 부릅니다.
다음은 학생별 총 대출 횟수를 나타내는 결과를 처리하는 FROM 부속질의 구문입니다.
SELECT v.name, v.total_loans
FROM (
SELECT s.name, COUNT(b.loan_id) AS total_loans
FROM students_table s
LEFT JOIN book_loans_table b ON s.student_id = b.student_id
GROUP BY s.name
) AS v
WHERE v.total_loans >= 2;
먼저 FROM 부속질의
결과를 가상 테이블로 가져옵니다. 이 부속질의 결과로 학생별 대출 수를 계산한 가상 테이블이 반환되고 이를 별칭 v
로 저장합니다.
그 후 주질의에서 조건별로 필터링해서 결과를 가져옵니다.
WHERE 부속질의(중첩질의)
는 WHERE
구문에서 사용되는 부속질의로 주질의의 WHERE
조건을 중첩질의 연산자
를 통해 연산하여 그 결과에 따라 주질의 출력 여부를 결정하게 됩니다.
다음은 부속질의 결과와 일치하는 학생(이수빈)이 빌린 책의 목록을 조회하는 중첩질의입니다.
SELECT book_title
FROM book_loans_table
WHERE student_id = (
SELECT student_id
FROM students_table
WHERE name = '이수빈'
);
중첩질의 결과와 일치하는 =
경우에만 출력을 하게됩니다.
중첩질의에서 사용되는 중첩질의 연산자에는 다음과 같은 것들이 있습니다.
술어 | 연산자 |
---|---|
비교 | =, >, >=, <, <= |
집합 | IN, NOT IN |
한정 | ALL, SOME |
존재 | EXISTS, NOT EXISTS |
부속질의
말고도 JOIN
구문을 사용하면 다음과 같이 누가 어떤 책을 대출했는지 알 수 있습니다.
'이수빈' 학생이 대출한 책의 제목과 학생 이름을 동시에 조회하는 쿼리를 각각 JOIN, 부속질의
로 작성하면 다음과 같습니다.
#JOIN 사용
SELECT s.name, b.book_title
FROM students_table s
JOIN book_loans_table b ON s.student_id = b.student_id
WHERE s.name = '이수빈';
#부속질의 사용
SELECT name, book_title
FROM book_loans_table
WHERE student_id = (
SELECT student_id
FROM students_table
WHERE name = '이수빈'
);
두 방식의 개념을 정리하면 다음과 같습니다.
JOIN | 부속질의 |
---|---|
여러 테이블을 하나의 테이블 집합으로 결합 | 다른 쿼리 결과를 하위의 쿼리로 중첩 후 조회 |
모든 테이블을 JOIN 한 후 나머지 쿼리 처리 | 부속 질의 수행 후 외부 질의 수행 |
둘 이상의 테이블 데이터를 하나로 연결 | 특정 조건에 맞는 데이터 탐색 or 비교 |
특히 JOIN
문을 MySQL과 같은 RDBMS에서 JOIN
을 위한 최적화 실행 플랜이 구축되어 있기에 대부분의 상황에서 부속질의보다 더 빠르고 효율적인 처리를 보여줍니다.
또한 INNER, LEFT, RIGHT, FULL JOIN
기능 활용을 통해 유연성과 가독성을 확보할 수 있어서 JOIN
을 사용하는 것이 더 좋은 경우가 많다라고 할 수 있습니다.
물론 지금 예시처럼
상관 서브쿼리
가 없는 경우에는부속질의
가 짧고 간결해서 더 우수한 경우도 있습니다.그러나 다음처럼
상관 서브쿼리(주질의의 테이블인 s의 student_id)
가 있는 경우 급격히 성능이 저하됩니다. 주질의 한 번마다 서브쿼리가 한 번씩 실행되는 구조이기 때문입니다.SELECT s.name, (SELECT COUNT(*) FROM book_loans_table b WHERE b.student_id = s.student_id) AS loan_count FROM students_table s;
그래서
부속질의
는 단순히 존재 확인, 아주 단순한 필터링 정도의 상황에서 보조적으로 사용하는 용도로 두고 대부분의 상황에서는JOIN
구문 사용을 권장드립니다.