서브 쿼리 Sub query
개념
- 다른 쿼리 내부에 포함된 SELECT 문을 말한다.
- 메인 쿼리를 보조하는 SELECT문이다.
- SQL 구문 실행 중에만 존재하는 가상의 테이블이다.
- 외부 쿼리 Outer query와 내부 쿼리 inner query로 구분된다.
- 외부 쿼리 : 내부 쿼리를 포함하고 있는 쿼리를 말한다. (= 부모 쿼리, 메인 쿼리 등)
- 내부 쿼리 = 서브 쿼리, 자식 쿼리 등으로 부른다.
- 서브 쿼리는 메인 쿼리와 종속 관계를 가진다. (메인 쿼리 ⊃ 서브 쿼리)
- 서브 쿼리를 우선 실행한 후 서브 쿼리의 결과값을 가지고 메인 쿼리를 실행한다.
- 서브 쿼리는 메인 쿼리의 컬럼을 사용할 수 있지만, 메인 쿼리는 서브 쿼리의 컬럼을 사용할 수 없다.
- 서브 쿼리는 메인 쿼리가 실행되기 전에 계산되기 때문에 메인 쿼리는 서브 쿼리의 컬럼에 접근할 수 없다.
- 같은 이유로, 서브 쿼리는 메인 쿼리의 컨텍스트 내에서 실행되기 때문에 메인 쿼리의 모든 컬럼을 참조할 수 있다.
- JAVA의 상속과 유사하다.
- 상속 : 부모 객체는 자식 객체의 인스턴스를 사용할 수 없지만 자식 객체는 부모 객체의 인스턴스를 사용할 수 있다.
예시 테이블
A 테이블
B 테이블
서브쿼리
1
2
3
4
5
6
7
| SELECT id, name
FROM A
WHERE EXISTS (
SELECT 1
FROM B
WHERE A.id = B.number;
)
|
메인 쿼리의 id를 서브 쿼리에서 참조한다. number과 id가 동일한 A 테이블의 row가 출력되며, 테이블 형태의 결과값은 다음과 같다.
하지만 아래와 같이 메인 쿼리에서 서브 쿼리를 참조하고자 하면 에러가 발생한다.
1
2
3
4
5
6
7
| SELECT a.id, b.number
FROM A a
WHERE a.id IN (
SELECT b.number
FROM B b
WHERE b.number > 2;
)
|
위 쿼리를 사용해 number가 2보다 큰 컬럼의 id와 number을 조회하고자 했지만 SQL ERROR : 존재하지 않는 필드 리스트 가 발생한다. 위 예시 테이블 외 실무에서 사용하고 있는 테이블에서 테스트했을 때는 Error Code: 1055. Expression #1 of SELECT list is not in GROUP BY clause and contains nonsegregated column ‘(컬럼명)’ which is not functionally dependent on columns in GROUP BY clause;가 뜨기도 했다.
장점
- SQL 구문 안에 SQL 구문을 만들 수 있어 간단하게 필터링이 가능하다. => 편리하다.
- 쿼리 구분이 명확하다.
- Join으로 해결할 수 없거나 Join 등의 방식이 복잡할 경우 사용할 수 있다.
단점
- 가상 테이블이기 때문에 실질적인 데이터를 저장하지 않는다. 이로 인해 서브 쿼리에 접근할 때마다 데이터를 만들어야 하기 때문에 연산 비용이 추가된다.
- 명시적 제약이나 인덱스 등 메타 정보가 존재하지 않기 때문에 최적화가 불가능하여 복잡한 연산을 수행하거나 결과 크기가 큰 서브 쿼리를 수행할 경우, 성능 문제가 발생할 수 있다.
- 쿼리 자체가 복잡해 한 번에 이해하기 힘들 수 있다.
조건
- select 문만 가능하다.
- 반드시 ( 괄호 ) 안에 존재해야 한다.
- 괄호 뒤에 ; 를 쓰지 않는다.
- ORDER BY 를 사용해 정렬할 수 없다.
- ORDER BY절은 SELECT 절에서 오직 하나만 올 수 있기 때문에 최상위 문장에 위치해야 한다.
- 스칼라 서브 쿼리의 경우, 결과값이 항상 단일값이기 때문에 ORDER BY가 무의미하다.
- 오라클 한정으로 인라인 뷰에서 ORDER BY 사용이 가능하다.
- MSSQL 한정으로 서브 쿼리에서 Top-N 구문과 함께 사용하면 ORDER BY 사용이 가능하다.
사용 가능 위치
- SELECT
- INSERT
- UPDATE
- DELETE
- SET
- DO
- FROM
- WHERE
- HAVING
- ORDER BY
동작 방식에 따른 분류
- 연관 서브 쿼리
- 서브 쿼리가 메인 쿼리의 컬럼을 가지고 있는 형태다.
- 논리적 실행 순서 : 메인 쿼리에서 읽힌 데이터에 대해 서브 쿼리에서 해당 조건이 만족하는지 확인하는 방식으로 수행된다.
- 비연관 서브 쿼리
- 서브 쿼리가 메인 쿼리 컬럼을 가지고 있지 않은 형태의 서브 쿼리를 말한다.
- 메인 쿼리에 서브 쿼리 결과값을 제공하기 위해 사용된다.
위치에 따른 분류
스칼라 서브 쿼리
- SELECT문에서 사용한다.
- 서브 쿼리를 하나의 컬럼처럼 사용한다.
- 하나의 레코드만 return 가능하다.
- NULL 값 return이 가능하다.
1
2
| SELECT a.id, a.name, (SELECT b.content FROM B b WHERE b.number = a.id) as content
FROM A a;
|
a.id | a.name | content |
---|
1 | A | 가 |
2 | B | 나 |
3 | C | 다 |
인라인 뷰
- FROM문에서 서브 쿼리를 하나의 테이블처럼 사용한다.
- 별칭 alias를 무조건 지정해야 한다.
1
2
3
4
5
| SELECT a.id, a.name, X.content
FROM A a,
(SELECT b.number, b.content
FROM B b) X
WHERE a.id = X.number;
|
a.id | a.name | content |
---|
1 | A | 가 |
2 | B | 나 |
3 | C | 다 |
서브 쿼리
- 조건절에서 사용한다.
- 일반적인 서브 쿼리로, 서브 쿼리를 하나의 변수나 상수처럼 사용하여 서브 쿼리의 결과에 따라 메인 쿼리의 결과값이 달라진다.
1
2
3
4
5
6
| SELECT id, name
FROM A
WHERE id IN (
SELECT number
FROM B
);
|
a.id | a.name | content |
---|
1 | A | 가 |
2 | B | 나 |
3 | C | 다 |
반환 데이터 형태에 따른 분류
단일 행 서브 쿼리
- 단일 행 비교 연산자와 함께 사용한다.
- 연산자 : =, <, >, <=, >=, <>
- 서브 쿼리의 결과 행 수가 1개 이하여야 하며, 2개 이상일 경우 “Subquery returned more than 1 value가 발생한다.
1
2
3
4
5
6
7
| SELECT id, name
FROM A
WHERE id = (
SELECT number
FROM B
WHERE content = '나'
);
|
다중 행 서브 쿼리
- IN , ANY, ALL, EXISTS 등의 연산자를 사용하여 얻은 서브 쿼리 결과 (복수 행)을 반환한다.
- 즉, 서브 쿼리의 결과로 여러 개의 컬럼이 반환되어 메인 쿼리의 조건과 비교되는 것을 말한다.
1
2
3
4
5
6
7
| SELECT a.id, a.name
FROM A a
WHERE a.id IN (
SELECT b.number
FROM B b
WHERE b.number > 2;
)
|
위 서브 쿼리의 경우, B 테이블의 number가 3 이상인 두 개의 행이 반환되나 A 테이블의 id가 3 까지만 있기 때문에 아래의 결과가 나타난다.
다중 컬럼 서브 쿼리
- 서브 쿼리의 결과값이 여러 개의 컬럼으로 이루어져 있는 것을 말한다.
- 메인 쿼리의 조건절에서 여러 컬럼을 동시에 비교할 수 있다.
- 서브 쿼리와 메인 쿼리에서 비교하는 컬럼의 수가 동일해야 한다.
- 서브 쿼리의 컬럼을 3개 조회하고, 메인 쿼리의 컬럼을 2개 조회한다면 에러가 발생할 수 있다.
1
2
3
4
5
6
7
| SELECT id, name
FROM A
WHERE (id, name) IN (
SELECT number, content
FROM B
WHERE number < 4
);
|
고려할 점
- 서브 쿼리를 통해 Join 시 스캔하는 레코드 수를 줄일 수 있을 경우 사용하는 것이 좋다.
참고
인파_ 님 - [MYSQL]서브쿼리 개념 & 문법 정리
song4 님 - 서브쿼리
예나부기 님 - 서브쿼리 & FROM절 서브쿼리 사용법
MariaDB [Why is ORDER BY in a FROM Subquery Ignored?]
소라고동_ 님 - [SQL] 성능 관점에서의 서브쿼리(Subquery)