SubQuery는 SQL을 작성하다 보면 빠질 수 없는 문법입니다.
서브쿼리에 종류와 특징을 파악하고 적절하게 사용할 줄 안다면 큰 도움이 될 것입니다.
SubQuery의 종류
1. 일반 서브쿼리 : 하나의 변수처럼 사용합니다. 쿼리 결과에 따라 다음과 같이 구분됩니다.
- 단일행 서브쿼리 (SingleRow Subquery) : 쿼리 결과가 단일행만을 리턴하는 서브쿼리입니다.
- 다중행 서브쿼리 (MultieRow Subquery) : 쿼리 결과가 다중행을 리턴하는 서브쿼리입니다.
- 다중칼럼 서브쿼리 (MultieColumn Subquery) : 쿼리 결과가 다중칼럼을 리턴하는 서브쿼리입니다.
2. 인라인 뷰 (Inline View) : 뷰 형태로써 테이블을 리턴하는 서브쿼리입니다. (테이블 대체)
FROM 내 쿼리 또는 JOIN 할 테이블을 리턴할 때 사용하는 서브쿼리입니다.
3. 스칼라 서브쿼리 (Scalar Subquery) : 하나의 칼럼처럼 사용되는 서브쿼리입니다.
* 상호 연관 서브쿼리 (Correlative Subquery) : 메인 쿼리의 값을 사용하는 서브쿼리입니다.
즉 메인 쿼리의 결과로 서브쿼리가 실행되고 그 결과로 메인쿼리가 또 실행되는 서로 연관된 쿼리를 의미합니다.
넘버링을 하지 않은 건 중복되는 분류이기 때문입니다. (스칼라 서브쿼리이면서 상호 연관 서브쿼리 일 수 있습니다.)
예제에 쓰일 테이블로 T_GRADE와 T_PERSON이 있습니다.
T_GRADE
PERSON_NO | GRADE |
1 | A |
2 | B |
3 | A |
T_PERSON
PERSON_NO | NAME | AGE |
1 | Aiden | 20 |
2 | Matthew | 20 |
3 | John | 19 |
일반 서브쿼리
일반적인 서브쿼리로써 변수처럼 사용합니다.
이름이 Aiden 인 사람의 GRADE를 얻으려면 어떻게 해야 할까요?
# 서브쿼리가 없을때
DECLARE @PERSON_NO INT
SELECT @PERSON_NO = A.PERSON_NO
FROM T_PERSON A
WHERE A.NAME = 'Aiden'
SELECT A.GRADE
FROM T_GRADE A
WHERE A.PERSON_NO = @PERSON_NO
# 서브쿼리 사용하면
SELECT A.GRADE
FROM T_GRADE A
WHERE A.PERSON_NO = (SELECT B.PERSON_NO
FROM T_PERSON B
WHERE B.NAME = 'Aiden')
이런 식으로 실제 'Aiden'의 PERSON_NO인 1 변수에 담아 조회할 때 사용하는 대신에
서브 쿼리로 이름이 'Aiden'인 사람의 PERSON_NO를 리턴하여서
T_GRADE의 WHERE절에 PERSON_NO의 조건으로 겁니다.
이때 서브 쿼리의 아웃풋의 종류에 따라
단일행 서브쿼리, 다중행서브 쿼리, 다중칼럼 서브쿼리로 나뉩니다.
위의 예제처럼 하나의 행만 리턴하는 경우를 단일행 서브쿼리(SingleRow Subquery)라고 합니다.
* 단일행을 리턴하기 때문에 단일행 비교 연산자 (=, >, <, <>) 만 사용해야 합니다.
다음은 GRADE가 A인 모든 사람을 출력하고 싶을 때
SELECT A.NAME
FROM T_PERSON A
WHERE A.PERSON_NO IN (SELECT B.PERSON_NO
FROM T_GRADE B
WHERE GRADE = 'A')
현재 GRADE 테이블에는 GRADE 가 'A'인 행이 2개이기 때문에 2줄의 행이 리턴됩니다.
위 예제처럼 여러 행을 리턴하는 경우를 다중행 서브쿼리(MultieRow Subquery)라고 합니다.
* 다중행을 리턴하기 때문에 다중행 비교 연산자 (IN, ANY, SOME, EXISTS 등)를 사용해야 합니다.
다음은 다중칼럼 서브쿼리 예제입니다.
SELECT *
FROM T_PERSON A
WHERE (A.NAME, A.AGE) IN (SELECT B.NAME, B.AGE
FROM T_PERSON B
WHERE B.PERSON_NO < 3)
위 예제처럼 두 개 이상의 칼럼을 리턴하는 경우를 다중칼럼 서브쿼리(MultieColumn Subquery)라고 합니다.
인라인 뷰 (Inline View)
인라인 뷰는 서브쿼리가 하나의 테이블처럼 사용되는 경우입니다.
굉장히 많이 쓰이는 문법이며 유용합니다.
#Subquery 없이 일반적인 테이블 Join
SELECT
FROM T_PERSON A
INNER JOIN T_GRADE B ON (B.PERSON_NO = A.PERSON_NO)
WHERE B.GRADE = 'A'
#Inline View Subquery
SELECT *
FROM T_PERSON A
INNER JOIN (
SELECT PERSON_NO
FROM T_GRADE A
WHERE A.GRADE = 'A'
) B ON (B.PERSON_NO = A.PERSON_NO)
위처럼 실제 테이블 대신에 서브쿼리의 결과를 테이블처럼 사용하는 경우를 인라인 뷰(Inline View)라고 합니다.
위 예제는 굉장히 단순하게 만들었지만 활용은 정말 무궁무진하고 많이 사용되는 쿼리입니다.
스칼라 서브쿼리 (Scalar Subquery)
스칼라 서브쿼리는 SELECT 절에서 서브쿼리를 사용하여 하나의 칼럼처럼 사용되는 쿼리를 의미합니다.
SELECT A.PERSON_NO
, A.NAME
, (SELECT G.GRADE
FROM T_GRADE G
WHERE G.PERSON_NO = A.PERSON_NO) AS GRADE
FROM T_PERSON A
칼럼의 역할을 하기 때문에 해당 서브쿼리는 반드시 한 개의 ROW만을 리턴해야 합니다.
위처럼 칼럼으로써 사용되는 서브쿼리를 스칼라 서브쿼리(Scalar Subquery)라고 합니다.
※ 스칼라 서브쿼리는 성능이 좋지도 않고 웬만하면 조인으로도 다 표현이 가능하니
꼭 필요 한경 우만 쓰시는 걸 장려합니다.
상호연관 서브쿼리 (Correlative Subquery)
상호연관 서브쿼리는 서브쿼리와 메인쿼리가 서로 연관돼있는 경우를 말합니다.
SELECT A.PERSON_NO
, A.NAME
, (SELECT G.GRADE
FROM T_GRADE G
WHERE G.PERSON_NO = A.PERSON_NO) AS GRADE
FROM T_PERSON A
스칼라 서브쿼리와 예제가 똑같은데요? 라고 하실 수 있겠습니다.
네 위 예제는 스칼라 서브쿼리이면서 동시에 상호연관 서브쿼리입니다.
위 서브쿼리의 G테이블은 WHERE절에서 메인쿼리의 A테이블과 비교하고 있습니다.
이런 식으로 메인쿼리의 값을 사용하는 서브쿼리를 상호연관 서브쿼리(Correlative Subquery)라고 합니다.
해당 서브쿼리는 메인쿼리의 값이 없으면 실행될 수 없습니다.
그래서 모든 행마다 서브쿼리와 메인쿼리가 정보를 주고받으며 연산합니다.
★★ 데이터양이 많다면 상당히 느려질 수 있습니다.
※ 스칼라 서브쿼리뿐만 아니라 인라인 뷰, 일반 서브쿼리도 메인쿼리와 값을 비교하면 상호연관 서브쿼리입니다.