섹션 8. 서브 쿼리(Sub-query) #
서브쿼리(Sub-query) #
- 서브쿼리는 하나의 쿼리 내에 또다른 쿼리가 포함되어있는 쿼리를 의미
- 서브 쿼리는 메인 쿼리(Main Query) 내에 포함되어있는 관계
- Where절에 사용될 경우 복잡한 업무적인 조건을 직관적인 SQL로 표현하여 필터링하는데 주로 사용됨
서브쿼리 유형 #
- 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);
- Select 절에 사용되는 서브쿼리 = 스칼라 서브쿼리
select ename, deptno,
(select dname from hr.dept x where x.deptno=a.deptno) as dname
from hr.emp a;
- 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) 서브쿼리
- 서브쿼리 내에 메인쿼리의 연결 컬럼을 가지고 있음
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;