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

 

SQL BOOSTER - 예스24

SQL BOOSTER는 프로젝트 성공을 위한 SQL 필독서다. 이 책은 마치 프로젝트를 진행하는 순서처럼 구성되어 있다. 프로젝트 투입을 위해 필요한 SQL 기술을 설명하고, 성능 테스트를 위해 필요한 기술

www.yes24.com