[Chapter 2] 09 SQL문 속 또 다른 SQL문, 서브쿼리

희진·2024년 4월 4일
post-thumbnail

09-1 서브쿼리

서브쿼리(SubQuery)란?

  • SQL문을 실행하는 데 필요한 데이터를 추가로 조회하기 위해 SQL문 내부에서 사용하는 SELECT문을 의미
  • 서브쿼리의 결과 값을 사용하여 기능을 수행하는 영역은 메인쿼리(Main Query)라고 부름
  1. 사원 이름이 JONES인 사원의 급여 출력하기
SELECT SAL
FROM EMP
WHERE ENAME = 'JONES';
SAL
2975
  1. 급여가 2975보다 높은 사원 정보 출력하기
SELECT *
FROM EMP
WHERE SAL > 2975;

  1. 서브쿼리로 JONES의 급여보다 높은 급여를 받는 사원 정보 출력하기
SELECT *
FROM EMP
WHERE SAL > (SELECT SAL
             FROM EMP
             WHERE ENAME = 'JONES');

SubQuery

서브쿼리의 특징

  1. 서브쿼리는 연산자와 같은 비교 또는 조회 대상의 오른쪽에 놓이며 괄호 ()로 묶어서 사용
  2. 특수한 경우를 제외한 대부분의 서브쿼리에서는 ORDER BY절을 사용할 수 없음
  3. 서브쿼리의 SELECT절에 명시한 열은 메인쿼리의 비교 대상과 같은 자료형과 같은 개수로 지정해야 함
  4. 즉 메인쿼리의 비교 대상 데이터가 하나라면 서브쿼리의 SELECT절 역시 같은 자료형인 열을 하나 지정해야 함
  5. 서브쿼리에 있는 SELECT문의 결과 행 수는 함께 사용하는 메인쿼리의 연산자 종류와 호환 가능해야 함
  6. 예를 들어 메인쿼리에 사용한 연산자가 단 하나의 데이터로만 연산이 가능한 연산자라면 서브쿼리의 결과 행 수는 반드시 하나여야 함

09-2 단일행 서브쿼리

서브쿼리에서 출력되는 결과가 하나이므로 메인쿼리와 서브쿼리 결과는 다음과 같이 단일행 연산자를 사용하여 비교

>>==<=<<>^=!=
초과이상같음이하미만같지 않음같지 않음같지 않음

단일행 서브쿼리와 날짜형 데이터

  • SCOTT보다 먼저 입사한 사원 목록 조회
SELECT *
FROM EMP
WHERE HIREDATE < (SELECT HIREDATE
                  FROM EMP
                  WHERE ENAME = 'SCOTT');

SubQuery

단일형 서브쿼리와 함수

  • 20번 부서에 속한 사원 중 전체 사원의 평균 급여보다 높은 급여를 받는 사원 정보와 소속 부서 정보를 함께 조회
SELECT E.EMPNO, E.ENAME, E.JOB, E.SAL, D.DEPTNO, D.DNAME, D.LOC
FROM EMP E, DEPT D
WHERE E.DEPTNO = D.DEPTNO
AND E.DEPTNO = 20
AND E.SAL > (SELECT AVG(SAL)
             FROM EMP);

SubQuery

09-3 다중행 서브쿼리

실행 결과 행이 여러 개로 나오는 서브쿼리. 다중행 연산자를 사용함.

  • IN : 메인쿼리의 데이터가 서브쿼리의 결과 중 하나라도 일치한 데이터가 있다면 true
  • ANY, SOME : 메인쿼리의 조건식을 만족하는 서브쿼리의 결과가 하나 이상이면 true
  • ALL : 메인쿼리의 조건식을 서브쿼리의 결과 모두가 만족하면 true
  • EXISTS : 서브쿼리의 결과가 존재하면(즉, 행이 1개 이상일 경우) true

IN 연산자

  • 각 부서별 최고 급여와 동일한 급여를 받는 사원 정보 출력하기
SELECT *
FROM EMP
WHERE SAL IN (SELECT MAX(SAL)
              FROM EMP
              GROUP BY DEPTNO);

IN

ANY, SOME 연산자

  • 서브쿼리가 반환한 여러 결과 값 중 메인쿼리와 조건식을 사용한 결과가 하나라도 true라면 메인쿼리 조건식을 true로 반환해 주는 연산자
  • 메인쿼리와 값을 비교할 때 ANY 및 SOME 연산자를 등가 비교 연산자(=)와 함께 사용하면 IN 연산자와 정확히 같은 기능 수행
  1. ANY 연산자 사용하기 (2와 결과 동일)
SELECT *
FROM EMP
WHERE SAL = ANY (SELECT MAX(SAL)
                 FROM EMP
                 GROUP BY DEPTNO);
  1. SOME 연산자 사용하기 (1과 결과 동일)
SELECT *
FROM EMP
WHERE SAL = SOME (SELECT MAX(SAL)
                 FROM EMP
                 GROUP BY DEPTNO);

ANY, SOME

  1. 30번 부서 사원들의 최대 급여보다 적은 급여를 받는 사원 정보 출력하기
SELECT *
FROM EMP
WHERE SAL < ANY(SELECT SAL
                FROM EMP
                WHERE DEPTNO = 30)
ORDER BY SAL, EMPNO;

<ANY

  • < ANY 연산자는 서브쿼리 결과 값 중 최댓값보다 작은 값은 모두 출력 대상
  • 따라서 서브쿼리에 MAX 함수를 적용한 값을 ANY 연산자 없이 비교 연산자(<)만 사용한 결과와 같은 효과
< ANY 연산자를 사용한 경우 서브쿼리에 MAX 함수를 사용한 경우
  1. 30번 부서 사원들의 최소 급여보다 많은 급여를 받는 사원 정보 출력하기
SELECT *
FROM EMP
WHERE SAL > ANY(SELECT SAL
                FROM EMP
                WHERE DEPTNO = 30)
ORDER BY SAL, EMPNO;

>ANY

  • > ANY 연산자는 서브쿼리 결과 값 중 최솟값보다 큰 값은 모두 출력 대상
  • 따라서 서브쿼리에 MIN 함수를 적용한 값을 ANY 연산자 없이 비교 연산자(>)만 사용한 결과와 같은 효과

ALL 연산자

서브쿼리의 모든 결과가 조건식에 맞아떨어져야만 메인쿼리의 조건식이 true가 되는 연산자
1. 부서 번호가 30번인 사원들의 최소 급여보다 더 적은 급여를 받는 사원 출력하기

SELECT *
FROM EMP
WHERE SAL < ALL (SELECT SAL
                 FROM EMP
                 WHERE DEPTNO = 30);

<ALL

  1. 부서 번호가 30번인 사원들의 최대 급여보다 더 많은 급여를 받는 사원 출력하기
SELECT *
FROM EMP
WHERE SAL > ALL (SELECT SAL
                 FROM EMP
                 WHERE DEPTNO = 30);

>ALL

EXISTS 연산자

서브쿼리에 결과 값이 하나 이상 존재하면 조건식이 모두 true, 존재하지 않으면 false가 되는 연산자
1. 서브쿼리에 결과 값이 존재하는 경우

SELECT *
FROM EMP
WHERE EXISTS (SELECT DNAME
              FROM DEPT
              WHERE DEPTNO = 10);

EXISTS

  1. 서브쿼리에 결과 값이 존재하지 않는 경우
SELECT *
FROM EMP
WHERE EXISTS (SELECT DNAME
              FROM DEPT
              WHERE DEPTNO = 50);

결과: 선택된 레코드가 없습니다.

09-4 다중열 서브쿼리

다중열 서브쿼리(Multiple-Column SubQuery)란?

서브쿼리의 SELECT절에 비교할 데이터를 여러 개 지정하는 방식

SELECT *
FROM EMP
WHERE (DEPTNO, SAL) IN (SELECT DEPTNO, MAX(SAL)
                        FROM EMP
                        GROUP BY DEPTNO);

Multiple-Column SubQuery

09-5 FROM절에 사용하는 서브쿼리와 WITH절

  • FROM절에 사용하는 서브쿼리는 인라인 뷰(Inline view)라고도 부름
  • 인라인 뷰는 특정 테이블 전체 데이터가 아닌 SELECT문을 통해 일부 데이터를 먼저 추출해 온 후 별칭을 주어 사용할 수 있음
  • FROM절에 너무 많은 서브쿼리를 지정하면 가독성이나 성능이 떨어질 수 있기 때문에 WITH절을 사용하기도 함
  1. 인라인 뷰 사용하기 (2와 결과 동일)
SELECT E10.EMPNO, E10.ENAME, E10.DEPTNO, D.DNAME, D.LOC
FROM (SELECT * FROM EMP WHERE DEPTNO = 10) E10,
     (SELECT * FROM DEPT) D
WHERE E10.DEPTNO = D.DEPTNO;
  1. WITH절 사용하기 (1과 결과 동일)
WITH -- 별칭 지정
E10 AS (SELECT * FROM EMP WHERE DEPTNO = 10),
D   AS (SELECT * FROM DEPT)
SELECT E10.EMPNO, E10.ENAME, E10.DEPTNO, D.DNAME, D.LOC
FROM E10, D
WHERE E10.DEPTNO = D.DEPTNO;

※ 상호 연관 서브쿼리

  • 메인쿼리에 사용한 데이터를 서브쿼리에서 사용하고 서브쿼리의 결과 값을 다시 메인쿼리로 돌려주는 방식인 상호연관 서브쿼리(Correlated SubQuery)도 존재
  • 다만 성능을 떨어뜨리는 원인이 될 수 있고 사용 빈도가 높지 않음
SELECT *
FROM EMP E1
WHERE SAL > (SELECT MIN(SAL)
             FROM EMP E2
             WHERE E2.DEPTNO = E1.DEPTNO)
ORDER BY DEPTNO, SAL;

09-6 SELECT절에 사용하는 서브쿼리

  • 스칼라 서브쿼리(Scala SubQuery)라고 부름
  • SELECT절에 명시하는 서브쿼리는 반드시 하나의 결과만 반환하도록 작성해야 함
SELECT EMPNO, ENAME, JOB, SAL,
       (SELECT GRADE
        FROM SALGRADE
        WHERE E.SAL BETWEEN LOSAL AND HISAL) AS SALGRADE,
        DEPTNO,
        (SELECT DNAME
         FROM DEPT
         WHERE E.DEPTNO = DEPT.DEPTNO) AS DNAME
FROM EMP E;

profile
열심히 살겠습니다

0개의 댓글