[MySQL] 부속질의

Bam·2025년 4월 21일
0

Database

목록 보기
23/39
post-thumbnail

부속질의 Subquery

부속질의 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 내부질의(스칼라 부속질의)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 구문에서 사용되는 부속질의로 부속질의 결과를 가상 테이블의 형태로 사용하기 때문에 인라인 뷰라고 부릅니다.

다음은 학생별 총 대출 횟수를 나타내는 결과를 처리하는 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 구문에서 사용되는 부속질의로 주질의의 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

VS JOIN

부속질의말고도 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 구문 사용을 권장드립니다.

0개의 댓글