이번 글에서는 SQL에서 잘 알려진 QUALIFY
문에 대해 알아보겠다. 특히, ROW_NUMBER()
나 RANK()
같은 윈도우 함수와 함께 자주 쓰이는 QUALIFY
를 통해 데이터를 더 세밀하게 필터링하는 방법을 살펴보자. 기본 SELECT 문에 아직 익숙하지 않다면, SQL (2) SELECT문 -데이터를 자유자재로 뽑아보자
■ QUALIFY란?
SQL의 QUALIFY
문은 WHERE
나 HAVING
처럼 필터링을 할 때 사용하는 문법이다. 다만 WHERE
가 행(row) 단위로 조건을 걸고, HAVING
이 그룹 단위로 조건을 걸 때, QUALIFY
는 윈도우 함수의 결과에 대해 조건을 걸어 필터링하는 데 사용된다. 즉, QUALIFY
는 윈도우 함수가 계산된 결과에 대해 추가적으로 조건을 부여할 때 매우 유용하다.
기본 구문은 다음과 같다.
SELECT 컬럼이름 FROM 테이블이름 QUALIFY 윈도우함수 = 조건;
아래와 같은 고객 주문 데이터가 있다고 해보자.
order_id | customer_id | order_date | total_amount |
1 | 101 | 2023-08-01 | 200 |
2 | 102 | 2023-08-05 | 150 |
3 | 101 | 2023-08-10 | 180 |
4 | 103 | 2023-07-20 | 300 |
5 | 102 | 2023-08-15 | 250 |
6 | 101 | 2023-08-20 | 100 |
7 | 103 | 2023-08-25 | 400 |
이 테이블에서 각 고객별로 가장 최근 주문을 조회하고 싶다면 어떻게 해야 할까? 여기서 ROW_NUMBER()
와 QUALIFY
를 함께 사용하여 가장 최신의 주문을 가져올 수 있다.
SELECT DISTINCT order_id, customer_id, order_date, total_amount FROM Customer_Orders QUALIFY ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY order_date DESC) = 1;
order_id | customer_id | order_date | total_amount |
6 | 101 | 2023-08-20 | 100 |
5 | 102 | 2023-08-15 | 250 |
7 | 103 | 2023-08-25 | 400 |
ROW_NUMBER()
는 고객별(customer_id
기준)로 주문 날짜(order_date
)에 따라 순번을 매긴다.PARTITION BY customer_id
는 고객별로 그룹을 나누는 역할을 하며, 각 그룹에서ORDER BY order_date DESC
를 통해 가장 최신 주문일을 기준으로 순서를 매긴다.QUALIFY ROW_NUMBER() = 1
은 각 고객의 최신 주문(순번이 1인 데이터)만 필터링한다.
■ QUALIFY와 RANK() 예시
이제 광고 캠페인 데이터를 관리한다고 가정해보자. 특정 고객이 여러 광고 캠페인에 참여했지만, 각 캠페인 중에서 가장 최신 데이터를 가져오고 싶다면 RANK()
를 사용할 수 있다.
campaign_id | customer_id | campaign_name | start_date | status |
1 | 101 | Summer Sale | 2023-06-01 | active |
2 | 102 | Winter Promo | 2023-07-15 | paused |
3 | 101 | Fall Launch | 2023-08-10 | active |
4 | 103 | New Year Sale | 2023-01-05 | inactive |
5 | 102 | Spring Offer | 2023-03-25 | active |
고객별로 가장 최신의 캠페인 상태를 조회하고 싶을 때, 다음과 같이 RANK()
를 사용하여 조회할 수 있다.
SELECT customer_id, campaign_name, start_date, status FROM Campaigns QUALIFY RANK() OVER (PARTITION BY customer_id ORDER BY start_date DESC) = 1;
customer_id | campaign_name | start_date | status |
101 | Fall Launch | 200 | active |
102 | Winter Promo | 150 | paused |
103 | New Year Sale | 300 | inactive |
RANK()
함수는 고객별로 캠페인 시작일을 기준으로 순위를 매긴다.PARTITION BY customer_id
로 고객별로 그룹을 나누고,ORDER BY start_date DESC
로 가장 최근 캠페인이 순위 1이 되도록 정렬한다.QUALIFY RANK() = 1
은 각 고객의 가장 최신 캠페인만 필터링하여 반환한다.
■ 실제 비즈니스 사례
1. 고객의 마지막 구매 이력 추출
소비자 행동 분석에서 각 고객의 마지막 구매 이력을 가져오는 것은 매우 중요한 작업이다. 예를 들어, 마케팅 캠페인을 위해 고객의 최신 구매 데이터를 바탕으로 리타겟팅할 때, ROW_NUMBER()
와 QUALIFY
를 이용하여 최신 주문만 추출할 수 있다.
SELECT DISTINCT customer_id, purchase_id, purchase_date, total_amount FROM Purchases QUALIFY ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY purchase_date DESC) = 1;
2. 이벤트 중복 로그 필터링
이벤트 로그 분석에서 중복된 이벤트나 불필요한 데이터를 걸러내야 할 때가 있다. 예를 들어, 동일한 이벤트에 대한 여러 로그가 기록되었지만, 가장 최근 로그만 남기고 싶다면 다음과 같은 쿼리를 사용할 수 있다.
SELECT DISTINCT event_id, user_id, event_timestamp FROM Event_Logs WHERE event_type = 'login' QUALIFY ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY event_timestamp DESC) = 1;
SQL의 QUALIFY
문은 윈도우 함수와 결합하여 데이터를 정밀하게 필터링하는 강력한 도구이다. ROW_NUMBER()
나 RANK()
와 함께 사용하면, 중복된 데이터를 효과적으로 제거하거나, 각 그룹에서 최신 데이터를 빠르게 추출할 수 있다. SELECT DISTINCT에 대해 더 자세히 알고 싶다면, SQL SELECT문 – 중복 제거와 특정 컬럼 제외하기
실제 비즈니스에서 고객 행동 분석이나 이벤트 로그 처리와 같은 작업에 매우 유용하게 적용할 수 있으며, 이를 통해 데이터의 품질을 높이고 분석의 효율성을 극대화할 수 있다.