005 Subquery

섹션 8. 서브 쿼리(Sub-query) #

서브쿼리(Sub-query) #

  • 서브쿼리는 하나의 쿼리 내에 또다른 쿼리가 포함되어있는 쿼리를 의미
  • 서브 쿼리는 메인 쿼리(Main Query) 내에 포함되어있는 관계
  • Where절에 사용될 경우 복잡한 업무적인 조건을 직관적인 SQL로 표현하여 필터링하는데 주로 사용됨

서브쿼리 유형 #

  1. Where절에 사용되는 서브쿼리
-- 평균 급여 이상의 급여를 받는 직원
select * from hr.emp where sal >= (select avg(sal) from hr.emp);

-- 가장 최근 급여 정보
select * from hr.emp_salary_hist a where todate = (select max(todate) from hr.emp_salary_hist x where a.empno = x.empno);
  1. Select 절에 사용되는 서브쿼리 = 스칼라 서브쿼리
select ename, deptno,
	(select dname from hr.dept x where x.deptno=a.deptno) as dname
from hr.emp a;
  1. From 절에 사용되는 서브쿼리 = 인라인 뷰
select a.deptno, b.dname, a.sum_sal
from
(
	select deptno, sum(sal) as sum_sal
	from hr.emp
	group by deptno
) a
join hr.dept b
on a.deptno = b.deptno;

서브쿼리 특징 #

  • 서브쿼리는 메인 쿼리에 where 조건으로 값을 전달하거나 메인쿼리와 연결되어 메인 쿼리의 필터링 작업을 수행
    • 서브쿼리 컬럼은 메인쿼리로 전달 불가능
    • 메인쿼리의 컬럼은 서브쿼리에서 사용 가능
  • 서브쿼리와 메인쿼리로 연결시 메인쿼리의 집합 레벨이 바뀌지않음
    • 메인쿼리와 서브쿼리 연결시 서브쿼리는 연결 컬럼으로 무조건 유니크한 집합, 즉 1의 집합이 되므로 메인쿼리의 집합 레벨을 변경하지않음
  • Order와 Order_Items는 order_id를 기준으로 하면 1:M 연결관계를 가진다.
    • 하지만 서브쿼리는 메인 쿼리 집합을 변화시키지 않으므로 결과 집합은 orders 집합 레벨이다.
select * from nw.orders a where order_id in (select order_id from nw.order_items where amount > 100);

서브쿼리 활용 #

  • 비상관(non-correlated) 서브쿼리
    • 서브쿼리 자체적으로 메인쿼리에 값을 전달
  • 상관(correlated) 서브쿼리
    • 서브쿼리 내에 메인쿼리의 연결 컬럼을 가지고 있음 img.png

Exists 연산자 #

  • 메인쿼리의 레코드 별로 서브쿼리의 결과가 한건이라도 존재하면 true가 되어 메인쿼리의 결과를 반환한다.
  • 문맥적으로 메인쿼리의 레코드가 서브쿼리에서 존재하는지를 체크하기 위해 활용

Not in vs Not Exists #

  • IN 연산자
    • 여러개의 값이 입력될 경우 개별 값의 = 조건들의 OR 연산이 적용된다.
SELECT * FROM HR.EMP WHERE DEPTNO IN (20, 30, NULL);
SELECT * FROM HR.EMP WHERE DEPTNO = 20 OR DEPTNO=30 OR DEPTNO = NULL;
  • NOT IN 연산자

    • 여러개의 개별 = 조건들이 NOT(조건=) AND NOT(조건=)로 변환되어 Null 적용시 직관가 다른 결과가 발생
    SELECT * FROM HR.EMP WHERE DEPTNO NOT IN (20, 30, NULL);
    SELECT * FROM HR.EMP WHERE DEPTNO != 20 AND DEPTNO != 30 AND DEPTNO != NULL;
    
    • AND DEPTNO != NULL; 조건 때문에 결과가 없다. (자체로 NULL이다.)
    • IS NOT NULL 이랑은 다름
    • 여러조건 결합시, True and Null 은 Null
    • 여러조건 결합시, True or Null은 True
  • NOT EXISTS 연산자

    SELECT A.*
    FROM NW.REGION A
    WHERE NOT EXISTS (SELECT SHIP_REGION FROM NW.ORDERS X WHERE X.SHIP_REGION = A.REGION_NAME --AND A.REGION_NAME IS NOT NULL
    );
    
    • 서브쿼리 반환값이 NULL이므로(반환값이 없다) NOT EXISTS 문은 true가 된다.
  • NOT EXISTS의 경우 NULL 값을 제외하려면 서브쿼리가 아닌 메인쿼리 영역에서 제외해야한다.

틀린 예제

SELECT A.*
FROM NW.REGION A
WHERE NOT EXISTS (SELECT SHIP_REGION FROM NW.ORDERS X WHERE X.SHIP_REGION = A.REGION_NAME AND A.REGION_NAME IS NOT NULL
                 );

올바른 예제

SELECT A.*
FROM NW.REGION A
WHERE NOT EXISTS (SELECT SHIP_REGION FROM NW.ORDERS X WHERE X.SHIP_REGION = A.REGION_NAME
                 )
AND A.REGION_NAME IS NOT NULL;

Null 조합 #

-- TRUE
SELECT 1=1;

-- FALSE
SELECT 1=2;

-- NULL
SELECT NULL = NULL;

-- NULL
SELECT 1=1 AND NULL;

-- NULL
SELECT 1=1 AND (NULL = NULL);

-- TRUE
SELECT 1=1 OR NULL;

-- FALSE
SELECT NOT 1=1;

-- NULL
SELECT NOT NULL;

스칼라 서브쿼리 #

  • Select 절에 사용할 수 있는 서브쿼리로 상관 서브쿼리와 유사하게 동작
  • 단 한 개의 로우, 단 한 개의 컬럼값만 반환할 수 있음.
  • 조인과 유사하게 from절의 집합과 연결되어 원하는 결과를 추출할 수 있으며, from절의 집합 레벨을 변화 시키지 않음.
  • From 절의 집합에서 한 건씩 스칼라 서브쿼리의 연결되어 값을 반환하므로 from절 집합이 커질 경우 수행 속도가 저하됨
  • 스칼라 서브쿼리는 Left Outer Join으로 대체 될 수 있음
SELECT A.*,
	(SELECT DNAME FROM HR.DEPT X WHERE X.DEPTNO=A.DEPTNO) AS DNAME
FROM HR.EMP A;

실습코드 #