본문 바로가기
스터디/SQL

[정리노트] SQLD 2024년 개정판 - 서브쿼리

by 견습생L 2024. 8. 21.

[정리노트] SQLD 2024년 개정판 - 서브쿼리


서브쿼리

  •   하나의 SQL 문안에 포함되어 있는 또 다른 SQL문
  •   반드시 괄호로 묶어야 함
    •   ex) SELECT안에 SELECT 문, INSERT, UPDATE, DELETE 안의 SELECT 문

 

사용 가능한 곳

  •   SELECT 절
  •   FROM 절
  •   WHERE 절
  •   HAVING 절
  •   ORDER BY 절
  •   기타 DML (INSERT, DELETE, UPDATE) 절
  •   GROUP BY 절 사용 불가

종류

동작 방식에 따라

  1. UN-CORRELATED (비연관) 서브쿼리
    •   서브쿼리가 메인쿼리 컬럼을 가지고 있지 않은 형태의 서브쿼리
    •   메인쿼리에 서브쿼리가 실행된 결과 값을 제공하기 위한 목적
  2. CORRELATED (연관) 서브쿼
    •   서브쿼리가 메인쿼리 컬럼을 ㅏ지고 있는 형태의 서브 쿼리
    •   일반적으로 메인쿼리가 먼저 수행된 후에 서브쿼리에서 조건이 맞는지 확인하고자 할 때 사용

 

위치에 따라

  1. 스칼라 서브쿼리
    •   SELECT에 사용하는 서브 쿼리
    •   서브쿼리 결과를 마치 하나의 컬럼처럼 사용하기 위해 주로 사용
    •   문법
      SELECT * | 컬럼명 | 표현식,
      	 (SELECT * | 컬럼명 | 표현식
      	    FROM 테이블명 또는 뷰명
      	   WHERE 조건)
        FROM 테이블명 또는 뷰명;
  2. 인라인뷰
    •   FROM 에 사용하는 서브쿼리
    •   서브쿼리 결과를 테이블처럼 사용하기 위해 주로 사용
    •   문법
      SELECT * | 컬럼명 | 표현식
        FROM (SELECT * | 컬럼명 | 표현식
          	  FROM 테이블명 또는 뷰명)
       WHERE 조건;


  3. WHERE 절 서브쿼리
    •   가장 일반적인 서브쿼리
    •   비교 상수 자리에 값을 전달하기 위한 목적으로 주로 사용 (상수항의 대체)
    •   리턴 데이터의 형태에 따라 단일행 서브쿼리, 다중행 서브쿼리, 다중컬럼 서브쿼리, 상호 연관 서브쿼리로 구분
    •   문법
      SELECT * | 컬럼명 | 표현식
        FROM 테이명 또는 뷰명
       WHERE 조건연산자 (SELECT * | 컬럼명 | 표현식
      					 FROM 테이블명 또는 뷰명
      					WHERE 조건);

WHERE 절 서브쿼리 종류

  1. 단일행 서브쿼리
    •   서브쿼리 결과가 1개의 행이 리턴되는 형태
    •   단일행 서브쿼리 연산자 종류

      연산자 의미
      = 같다
      <> 같지 않다
      > 크다
      >= 크거나 같다
      < 작다
      <= 작거나 같다


    •   ex) EMP 테이블에서 전체 직원의 급여 평균보다 높은 평균을 받는 직원의 정보 출력
    •   STEP 1. 비교대상(전체 직원 급여 평균) 확인
      SELECT AVG(SAL)
        FROM EMP;


    •   STEP 2. 메인쿼리에 비교 상수 자리에 서브쿼리 결과 전달
      SELECT EMPNO, ENAME, SAL
        FROM EMP
       WHERE SAL > (SELECT AVG(SAL)
                      FROM EMP);


  2. 다중행 서브쿼리
    •   서브쿼리 결과가 여러 행이 리턴되는 형태
    •   ‘=’, ‘>’, ‘<’ 와 같은 비교 연산자 사용불가(여러 값이랑 비교할 수 없는 연산자들)
    •   서브쿼리 결과를 하나로 요약하거나 다중행 서브쿼리 연산자를 사용
    •   다중행 서브쿼리 연산자
      연산자 의미
      IN 같은 값을 찾음
      > ANY 최소값을 반환함
      < ANY 최대값을 반환함
      < ALL 최소값을 반환함
      > ALL 최대값을 반환함
    •   ex) ALL 과 ANY 비교
      > ALL(2000, 3000) : 최대값(3000)보다 큰 행들 반환
      < ALL(2000, 3000) : 최소값(2000)보다 작은 행들 반환
      > ANY(2000, 3000) : 최소값(2000)보다 큰 행들 반환
      < ANY(2000, 3000) : 최대값(3000)보다 작은 행들 반환
    •   ex) 다중행 서브쿼리 연산자 오류 (서브쿼리 결과가 여러 개일 경우 = 연산자와 대소 비교 불가)
      SELECT EMPNO, ENAME, SAL
        FROM EMP
       WHERE SAL > (SELECT SAL
                      FROM EMP
                     WHERE DEPTNO = 10);
    •   해결 1: 서브쿼리 결과를 하나의 행의 결과가 되도록 변경
      SELECT EMPNO, ENAME, SAL
        FROM EMP
       WHERE SAL > (SELECT MIN(SAL)
                      FROM EMP
                     WHERE DEPTNO = 10);


    •   해결 2: 다중행 서브쿼리 연산자로 변경
      SELECT EMPNO, ENAME, SAL
        FROM EMP
       WHERE SAL > ANY(SELECT SAL
                         FROM EMP
                        WHERE DEPTNO = 10);


  3. 다중컬럼 서브쿼리
    •   서브쿼리 결과가 여러 컬럼이 리턴되는 형태
    •   메인쿼리와의 비교 컬럼이 2개 이상
    •   대소 비교 전달 불가 (두 값을 동시에 묵어 대소 비교 할 수 없음)

    •   ex) EMP 테이블에서 부서별 최대 급여자 확인
    •   STEP 1. 부서별 최대 급여 확인
      SELECT DEPTNO, MAX(SAL)
        FROM EMP
       GROUP BY DEPTNO;


    •   STEP 2. 메인쿼리에 비교 대상으로 서브쿼리 결과 전달
      SELECT EMPNO, ENAME, SAL, DEPTNO
        FROM EMP
       WHERE (DEPTNO, SAL) IN (SELECT DEPTNO, MAX(SAL)
                                 FROM EMP
                                GROUP BY DEPTNO);

      → 부서별 최대 급여가 여러 값이 나오므로 비교 시에는 다중행 연산자인 IN을 사용 (= 사용 시 에러발생)

  4. 상호연관 서브쿼리
    •   메인 쿼리와 서브쿼리의 비교를 수행하는 형태
    •   비교한 집단이나 조건은 서브쿼리에 명시(메인쿼리절에는 서브쿼리 컬럼이 정의 되지 않았기 때문에 에러 발생)

    •   ex) EMP 테이블에서 부서별로 해당 부서의 평균급여보다 높은 급여를 받는 사원 정보
      ** 에러 발생 다중 컬럼 서브쿼리는 동시 두 컬럼에 대한 대소비교 불가
      SELECT EMPNO, ENAME, SAL, DEPTNO
        FROM EMP
       WHERE (DEPTNO, SAL) > (SELECT DEPTNO, AVG(SAL)
                                FROM EMP
                               GROUP BY DEPTNO);


    •   해결: 대소 비교할 컬럼을 메인쿼리에, 일치 조건을 서브쿼리에 전달
      SELECT EMPNO, ENAME, SAL, DEPTNO
        FROM EMP E1
       WHERE SAL > (SELECT AVG(SAL)
                      FROM EMP E2
                     WHERE E1 DEPTNO = E2.DEPTNO
                     GROUP BY DEPTNO);


      → 메인쿼리와 결과적으로 비교해야 할 컬럼은 .SAL 과 DEPTNO 인데, 그 중 SAL에 대한 대소비교 전에 먼저 비교할 부서(DEPTNO) 정보가 확정돼야 함
      → 메인쿼리에는 서브쿼리의 테이블 정보가 없으므로(순서상 메인쿼리부터 해석) E.DEPTNO = D.DEPTNO 조건은 사용 불가)
      → 먼저 비교할 DEPTNO 겂을 서브쿼리에 전달

    •   ** 상호 연관 서브쿼리 연산 순서
      1. 메인쿼리 테이블 READ
      2. 메인쿼리 WHERE 절 확인 (SAL 확인)
      3. 서브쿼리 테이블 READ
      4. 서브쿼리 WHERE 절 확인 (다시 E1.DEPTNO 요구)
      5. E1.DEPTNO 겂을 서브쿼리의 DEPTNO 컬럼과 비교하여 조건절 완성
      6. 위 조건에 성립하는 행의 그룹연산 결과 확인 (AVG(SAL))
      7. 위 결과를 메인쿼리에 전달하여 해당 조건을 만족하는 행만 추출
        ※ 상호연관 서브쿼리 사용 시 GROUP BY 생략 가능

 

 

인라인 뷰

  •   인라인뷰
    •   쿼리 안의 뷰 형태로 테이블처럼 조회할 데이터를 정의하기 위해 사용
    •   테이블명이 존재하지 않기 때문에 다른 테이블과 조인 시 반드시 테이블 별칭 명시 (단독으로 사용하는 경우 불필요)
    •   WHERE 절 서브쿼리와 다르게 서브쿼리 결과를 메인쿼리의 어느 절에서도 사용할 수 있음
    •   인라인뷰의 결과와 메인쿼리 테이블과 조인할 목적으로 주요 사용
    •   모든 연산자 사용 가능 
    •   ex) EMP 테이블에서 부서별 최대 급여자를 출력하되, 최대 급여와 함께 출력
      SELECT E.EMPNO, E.ENAME, E.SAL, I.MAX_SAL
        FROM EMP E, (SELECT DEPTNO, MAX(SAL) AS MAX_SAL
                       FROM EMP
                      GROUP BY DEPTNO) I
       WHERE E.DEPTNO = I.DEPTNO
         AND E.SAL = I.MAX_SAL;

      → 인라인뷰에서의 함수에 의한 출력결과 (MAX(SAL))는 럼 별칭을 통해 메인쿼리에 전달
         (그룹함수는 where 절에 들어갈 수 없음, 단일 함수만 사용 가능)

    •   ex) EMP 테이블에서 부서별로 해당 부서의 평균 급여보다 높은 급여자를 출력하되, 평균 급여와 함께 출력
      SELECT E.EMPNO, E.ENAME, E.SAL, I.AVG_SAL
        FROM EMP E, (SELECT DEPTNO, AVG(SAL) AS AVG_SAL
                       FROM EMP
                      GROUP BY DEPTNO) I
       WHERE E.DEPTNO = I.DEPTNO
         AND E.SAL > I.AVG_SAL;

 

 

스칼라 서브쿼리

  •   스칼라 서브쿼리
    •   SELECT 절에 사용하는 쿼리로, 마치 하나의 컬럼처럼 표현하기 위해 사용(단 하나의 출력 대상만 표현 가능)
    •   각 행마다 스칼라 서브쿼리 결과가 하나여야 함 (단일행 서브쿼리 형태)
    •   조인의 대체 연산
    •   스칼라 서브쿼리를 사용한 조인 처리 시 OUTER JOIN이 기본 (값이 없더라도 생략되지 않고 NULL로 출력됨)
    •   ex) EMP의 각 직원의 사번, 이름과 부서이름을 출력 (부서이름을 스칼라 서브쿼리로)
      SELECT EMPNO, ENAME,
            (SELECT DNAME
               FROM DEPT D
              WHERE D.DEPTNO = E.DEPTNO) AS DNAME
        FROM EMP E
       WHERE DEPTNO = 10;


    •   ex) EMP의 각 직원의 사번, 이름, 부서번호, 급여와 함께 급여 총합을 출력 (총합을 스칼라 서브쿼리로)
      SELECT EMPNO, ENAME, DEPTNO, SAL
            (SELECT SUM(SAL)
               FROM EMP) AS SUM_SAL
        FROM EMP;
    • ex) 서브쿼리의 아우터 조인
       SELECT E1.ENAME AS 사원명
             (SELECT E2.ENAME
                FROM EMP E2
               WHERE E1.MGR = E2.EMPNO) AS 상위관리자명
         FORM EMP E1;
       
      → KING의 경우 MGR 컬럼 값이 NULL 이므로 MGR = EMPNO에 만족하는 E2.ENMAE 값이 없지만, 스칼라 서브쿼리는 무조건 메인쿼리절이 출력하는 대상에 대해 항상 값을 리턴해야하므로 생랴괴디 않고 NULL 출력
  •   서브쿼리 주의 사항
    •   특별한 경우 (TOP-N 분석 등)를 제외하고는 서브 쿼리절에 ORDER BY 절 사용 불가
    •   단일 행 서브쿼리와 다중 행 서브쿼리에 따라 연산자의 선택이 중요
    •   ex) 서브쿼리에 ORDER BY 전달 시 에러 발생
      SELECT *
        FROM EMP
       WHERE SAL IN (SELECT SAL
                       FROM EMP
                      WHERE DEPTNO = 10
                      ORDER BY SAL);

 

반응형