DataBase/SQL
SQL BOOSTER - 서브쿼리 (WHERE)
now0204
2024. 6. 23. 15:33
1. WHERE절 단독 서브쿼리
서브쿼리는 WHERE 절에서도 단독으로 사용할 수 있다.
1.1 마지막 한 건 조회하기
SELECT *
FROM T_ORD T1
WHERE T1.ORD_SEQ = (SELECT MAX(A.ORD_SEQ) FROM T_ORD A);
위 SQL은 T_ORD 테이블을 두 번 사용하고 있다. SQL의 성능을 위해서는 테이블의 반복 출현을 줄여야한다.
따라서 위 SQL은 아래와 같이 사용하는 것이 성능 면에서 좀 더 유리할 수 있다.
-- order by와 rownum 활용
SELECT *
FORM (
SELECT *
FROM T_ORD T1
ORDER BY T1.ORD_SEQ DESC
)A
WHERE ROWNUM <= 1;
- ORDER BY와 ROWNUM을 사용한 방법이다. -> 해당 방법에서 성능에 이득을 보려면, ORD_SEQ에 대한 인덱스가 필수이다. T_ORD 테이블은 ORD_SEQ가 PK이므로 이미 인덱스가 존재한다. (PK에는 기본적으로 UNIQUE 인덱스가 생성된다)
1.2 마지막 일자 주문 조회하기
마지막 주문 일자를 조회하는 SQL이다.
SELECT * FROM T_ORD T1
WHERE T1.ORD_DT = (SELECT MAX(ORD_DT) FROM T_ORD A);
위 SQL은 성능을 위해 ORD_DT 칼럼에 인덱스가 필요할 수 있다.
1.3 IN조건에 서브쿼리 사용하기
SELECT *
FROM T.ORD T1
WHERE T1.ORD_DT >= TO_DATE('20170301','YYYYMMDD')
AND T1.ORD_DT < TO_DATE('20170401','YYYYMMDD')
AND T1.CUS_ID ID (
SELECT A.CUS_ID
FROM T.ORD A
WHERE A.ORD_DT >= TO_DATE('20170301','YYYYMMDD')
AND A.ORD_DT < TO_DATE('20170401','YYYYMMDD')
GROUP BY A.CUS_ID
HAVING COUNT(*) >= 4);
--인라인 뷰를 활용하기
SELECT T1.*
FORM T_ORD T1, (
SELECT A.CUS_ID
FROM T_ORD A
WHERE A.ORD_DT >= TO_DATE('20170301','YYYYMMDD')
AND A.ORD_DT < TO_DATE ('20170401','YYYYMMDD')
GROUP BY A.CUS_ID
HAVING COUNT(*) >=4
)T2
WHERE T1.ORD_DT >= TO_DATE('20170301','YYYYMMDD')
AND T1.ORD_DT < TO_DATE ('20170401','YYYYMMDD')
AND T1.CUS_ID = T2.CUS_ID;
- 위 SQL은 중복된 데이터를 찾아낼 때도 유용하게 사용할 수 있는 패턴이다.
- 서브쿼리 안에서 GROUP BY를 한 이유는 쿼리 결과로 조건에 해당하는 모든 데이터 출력을 위함
2. WHERE절 상관 서브쿼리
WHERE 절의 상관 서브쿼리는 데이터의 존재 여부를 파악할 때 자주 사용한다.
예를 들어 특정 일자나 특정 월에 주문이 존재하는 고객 리스트 같은 것을 뽑을 때 유용하다.
SELECT *
FROM M_CUS T1
WHERE EXISTS(
SELECT *
FROM T_ORD A
WHERE A.CUS_ID = T1.CUS_ID
AND A.ORD_DT >= TO_DATE('20170301','YYYYMMDD')
AND A.ORD_DT < TO_DATE('20170401','YYYYMMDD')
);
- 위 SQL은 3월에 주문이 한 건이라도 존재하는 고객을 조회한다. 반대로 주문이 한 건도 없는 고객을 조회해야 한다면, NOT EXISTS를 사용하면 된다. 이처럼 WHERE 절의 상관 서브 쿼리는 다른 테이블에 데이터 존재 여부를 파악할 때 유용하다.
WHERE절 서브쿼리 안에서도 조인을 사용할 수 있다.
SELECT *
FROM M_CUS T1
WHERE EXISTS(
SELECT *
FROM T_ORD A
,T_ORD_DET B
,M_ITM C
WHERE A.CUS_ID = T1.CUS_ID
AND A.ORD_DT >= TO_DATE('20170301','YYYYMMDD')
AND A.ORD_DT < TO_DATE('20170401','YYYYMMDD')
AND A.ORD_SEQ = B.ORD_SEQ
AND B.ITM_ID = C.ITM_ID
AND C.ITM_TP = 'ELEC');
- 3월에 아이템 유형이 ELEC인 주문이 한 건이라도 존재하는 고객 정보를 조회하는 쿼리이다.
*참고 EXISTS는 SELECT절의 서브쿼리에도 사용이 가능함
고객 리스트 조회 + 고객의 3월 주문 존재 여부를 YN으로 보여주어야 한다면, 다음과 같이 작성할 수 있다.
SELECT T1.CUS_ID, T1.CUS_NM,
(CASE WHEN
EXISTS(
SELECT *
FROM T_ORD A
WHERE A.CUS_ID = T.CUS_ID
AND A.ORD_DT >= TO_DATE('20170301','YYYYMMDD')
AND A.ORD_DT < TO_DATE('20170401','YYYYMMDD')
)
THEN 'Y'
ELSE 'N' END) ORD_YM_03
FROM M_CUS T1;
- CASE문과 EXISTS절을 사용해 3월에 주문이 존재하면 Y 주문이 없으면 N이 조회되도록 처리하고 있다.
- 위 SQL은 인라인 뷰와 아우터 조인을 사용해 해결할 수도 있는데, 어떤 방법이던지 성능에 차이가 존재할 수 있다 SQL 작성과 함께 조회될 고객과 주문의 건수 그리고 인덱스에 따라 성능 차이가 발생한다.
참고자료
https://www.yes24.com/Product/Goods/82818767