-
SQL BOOSTER - 서브쿼리 (WHERE)DataBase/SQL 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
'DataBase > SQL' 카테고리의 다른 글
SQL BOOSTER - MERGE 조인과 성능 (0) 2024.06.24 SQL BOOSTER - MERGE (0) 2024.06.23 SQL BOOSTER - 서브쿼리 (SELECT) (0) 2024.06.23 SQL BOOSTER - OUTER JOIN (0) 2024.06.20 SQL BOOSTER - OUTER JOIN (0) 2024.06.20