DB/SQL - Oracle

[SQL] Sub-Query(서브쿼리) - 스칼라, 인라인 뷰, 중첩 서브쿼리

생각많은 프로그래머 2024. 2. 16. 22:15

 

서브 쿼리(Sub-Query)란,

한 쿼리문 안에 포함된 또 다른 SELECT 문을 의미한다.

 

최종 결과를 출력하는 쿼리를 메인쿼리라고 한다면,

이를 위한 중간 단계 혹은 보조 역할을 하는

SELECT문을 서브쿼리라 한다. 

 

***메인쿼리는 SELECT문 외에도 

INSERT, UPDATE, MERGE, DELETE문도 가능하다.

하지만 서브쿼리는 SELECT문만 가능하다.

서브쿼리의 특징

 

메인쿼리와 서브쿼리의 관계는 주종 관계로서,

서브쿼리에 작성된 컬럼명은 메인쿼리의 컬럼명을 가져와서

사용할 수 있으나 그 역은 성립하지 않는다.

 

하나의 SQL 문을 기준으로 메인쿼리를 제외한 나머지 

모든 SELECT문을 서브쿼리로 보면 된다. 

 

하나의 메인쿼리 안에는 1개 이상의 서브쿼리를 사용할 수 있다.

(물론 필요없으면 안 써도 된다!)

 

실행 순서로 보면 서브쿼리가 먼저 실행되고

이후 메인쿼리가 실행된다. 

 

서브쿼리의 결과를 메인쿼리에 적용하므로

서브쿼리의 리턴값(결과값)이

메인쿼리의 매개변수라고 볼 수도 있겠다.

 

서브쿼리의 종류

 

서브쿼리는 동작하는 방식이나 반환되는 데이터의 형태, 

활용되는 위치를 기준으로 분류할 수 있다. 

 

먼저 동작 방식을 기준으로 분류한 서브쿼리의 유형은 다음과 같다.

서브쿼리 종류 설명
연관 서브쿼리
(Un-Correlated
Sub-Query)
- 서브쿼리가 메인쿼리의 컬럼을 가지고 있지 않은 형태
- 메인 테이블과 조인 조건이 걸리지 않는 서브쿼리를 말함
- 서브쿼리는 메인쿼리 없이 독자적으로 실행됨

- 메인쿼리에 서브쿼리에서 실행된 결과를 제공하는 용도로 사용
연관 서브쿼리
(Correlated
Sub-Query)
- 서브쿼리가 메인쿼리의 컬럼을 가지고 있는 형태
- 메인 테이블과 조인 조건이 걸린 서브쿼리를 말함

- 메인쿼리가 먼저 수행되어 얻은 데이터를 서브쿼리의 조건에
  맞는지 확인하는 용도로 사용

 

반환되는 데이터 형태에 따른 서브쿼리의 종류는 다음과 같다.

서브쿼리 종류 설명
단일 행 서브쿼리
(Single Row
Sub-Query)
- 서브쿼리의 결과가 항상 1건 이하인 서브쿼리
- 단일 행의 비교 연산자에는 =, <, <=, >, >=, <> 등을 사용
다중 행 서브쿼리
(Multiple Row
Sub-Query)
- 서브쿼리의 실행 결과가 여러 건인 서브쿼리
- 다중 행 비교 연산자 사용
IN : 서브쿼리의 결과에 존재하는 임의의 값과 동일한 조건을 의미
ALL : 서브쿼리의 결과에 존재하는 모든 값을 만족하는 조건을 의미
ANY : 서브쿼리의 결과에 존재하는 어느 하나의 값이라도 만족하는 조건을 의미
EXIST : 서브쿼리의 결과를 만족하는 값이 존재 여부를 확인하는 조건을 의미
(다중 행 서브쿼리를 사용할 때는 다중 행 비교 연산자를 사용해야 함.
단일 행 서브쿼리를 사용할 때는 단일 행, 다중 행 비교 연산자 모두 사용가능함)
다중 컬럼 서브쿼리
(Single Column
Sub-Query)
- 서브쿼리의 결과가 여러 컬럼으로 반환되는 서브쿼리
- 메인쿼리의 조건절에 여러 컬럼을 동시에 비교할 때, 
서브쿼리와 메인쿼리에서 비교하는 컬럼 개수와 위치가 동일해야 함

 

서브쿼리의 위치를 기준으로도 서브쿼리를 구분할 수 있다. 

서브쿼리 종류 설명
일반 서브쿼리
(= 스칼라 서브쿼리)
(Scalar Sub-Query)
- 서브쿼리의 위치가 SELECT 절에 있는 경우
- 단일 행 서브쿼리만 올 수 있음
- 컬럼의 튜플 수만큼 반복실행되므로 결과 산출까지 시간이 오래 걸림
인라인 뷰
(Inline Views)
- 서브쿼리의 위치가 FROM 절에 있는 경우
- VIEW 처럼 결과가 동적으로 생성된 테이블 형태로 사용할 수 있음
중첩 서브쿼리
(Nested Sub-Query)
- 서브쿼리의 위치가 WHERE 절이나 HAVING 절에 있는 경우

 

 

예시를 통해 살펴보자.

//employees 테이블 생성
CREATE TABLE employees (
    employee_id NUMBER(5) PRIMARY KEY,
    employee_name VARCHAR2(50),
    department_id NUMBER(3),
    salary NUMBER(10, 2)
);

//employees 테이블에 데이터 삽입
INSERT INTO employees 
VALUES (1, 'John Doe', 101, 50000);

INSERT INTO employees 
VALUES (2, 'Jane Smith', 102, 60000);

INSERT INTO employees 
VALUES (3, 'Alice Johnson', 101, 55000);

INSERT INTO employees 
VALUES (4, 'Bob Williams', 103, 62000);

INSERT INTO employees 
VALUES (5, 'Mary Brown', 102, 58000);
예시 1 : 스칼라, 중첩 서브쿼리

부서별 평균 급여를 조회하고, 해당 부서의 평균 급여보다

높은 급여를 받는 직원을 조회한다. 

출력되는 순서는 직원ID, 직원명, 급여, 소속 부터의 평균 급여이다.

SELECT 
    employee_id,
    employee_name,
    salary,
    (SELECT AVG(salary)
     FROM employees
     WHERE department_id = e.department_id
    ) AS avg_department_salary
FROM employees e
WHERE salary > (
    SELECT AVG(salary)
    FROM employees
    WHERE department_id = e.department_id
);

 

 

예시 2 : 인라인 뷰

부서별 평균 급여와 각 직원의 급여를 조회한다.

출력하려는 컬럼은 직원ID, 직원명, 급여, 소속부서의 평균급여이다.

SELECT e.employee_id, e.employee_name, e.salary, d.avg_salary
FROM employees e
JOIN (
    SELECT department_id, AVG(salary) AS avg_salary 
    FROM employees 
    GROUP BY department_id) d
ON e.department_id = d.department_id;

 

 

예시 3 : 중첩 서브쿼리

부서별 최고 급여를 받는 직원을 조회한다.

출력하려는 컬럼은 직원ID, 직원명, 급여이다.

SELECT 
    employee_id, 
    employee_name, 
    salary
FROM 
    employees e1
WHERE 
    salary = (
        SELECT MAX(salary)
        FROM employees e2
        WHERE e1.department_id = e2.department_id
    );