PromleeBlog
sitemap
aboutMe

posting thumbnail
SQL 조회 문법 총정리 4편 (MySQL/Oracle 정규식, 윈도우 함수 심화, 피벗)
Advanced SQL Query Syntax Part 4 (MySQL/Oracle Regex, Advanced Window Functions, Pivot)

📅

🚀

들어가기 전에 🔗

안녕하세요. 4편에서는 더욱 강력하고 섬세한 데이터 처리를 가능하게 하는 기법들을 함께 마스터해보겠습니다.
이번 편에서는 까다로운 문자열 패턴을 자유자재로 다룰 수 있는
정규표현식
, 데이터 분석의 꽃이라 불리는
윈도우 함수의 심화된 활용법
, 행과 열을 유연하게 전환하는
피벗팅과 언피벗팅
, 그리고 다양한 관점에서 데이터를 집계하는 CUBEGROUPING SETS와 같은 고급 집계 함수들을 집중적으로 살펴보겠습니다.
1, 2편에서 사용했던 Users 테이블과 Orders 테이블을 계속 사용합니다.

🚀

집계 함수 활용 🔗

이번에는 GROUP BY 절과 함께 사용되어 더 다양한 조합의 소계를 생성하는 CUBEGROUPING SETS를 알아보겠습니다.

CUBE: 모든 가능한 조합의 소계 및 총계 생성 🔗

ROLLUP이 계층적인 소계를 제공한다면, CUBE는 지정된 그룹핑 컬럼들의 모든 가능한 조합에 대한 소계와 총계를 생성합니다.

예시
사용자(Users.name)별, 상품(Orders.product)별 주문 총액 및 각 요소별 소계, 전체 총액 보기 (Oracle 기준)
user_nameproduct_nametotal_sales
이동훈Banana1500
이동훈NULL1500
이동훈NULL1500
홍길동Apple6000
...
(결과는 (사용자, 상품), (사용자, 모든 상품), (모든 사용자, 상품), (모든 사용자, 모든 상품)의 조합에 대한 합계를 보여줍니다.)
MySQL에서는 이 기능을 구현하려면 여러 GROUP BY 쿼리를 UNION ALL로 연결해야 하므로 코드가 매우 길어집니다.

GROUPING SETS: 원하는 그룹핑 조합만 선택적으로 집계 🔗

ROLLUP이나 CUBE보다 더 유연하게, 개발자가 명시적으로 원하는 그룹핑 조합에 대해서만 소계를 계산합니다.

예시
(사용자별, 상품별 총액), (사용자별 총액), (상품별 총액), (전체 총액)을 한 번에 보기 (Oracle 기준)
user_nameproduct_nametotal_salesgrouping_level
이동훈Banana15000
이동훈NULL15001
이동훈NULL15003
홍길동Apple60000
...
(결과는 각 GROUPING SETS에 지정된 조합별로 집계된 행들을 보여줍니다.)
MySQL에서는 각 GROUP BY 결과를 UNION ALL로 합쳐야 합니다.

🚀

정규표현식(Regular Expressions)으로 강력한 문자열 처리 🔗

복잡한 패턴 매칭, 특정 형식의 문자열 검색 및 추출, 문자열 대체 등 일반적인 LIKE 절이나 문자열 함수로는 처리하기 어려운 작업을 정규표현식을 통해 수행할 수 있습니다.

예시 1
이메일 주소가 'example.com' 또는 'another.com'으로 끝나고, '@' 앞에 영문자/숫자/밑줄/점/하이픈만 허용하는 사용자 찾기
nameemail
홍길동hong@example.com
제임스james@example.com
이몽룡mong@another.com

예시 2
상품명(product)에서 괄호 () 안의 내용만 추출하기 (예: 'Product A (정보)' -> '정보')
productdetail
Apple (Red)Red
Banana (Yellow)Yellow
(실제 데이터가 있어야 결과가 나옵니다. 예시 데이터에는 괄호 포함 상품이 없습니다.)

🚀

윈도우 함수 심화 활용 (Window Functions) 🔗

2편과 3편에서 배운 윈도우 함수에 더해, 데이터 분할 및 프레임 지정을 통한 고급 분석 기법을 알아봅니다.

NTILE: 데이터를 N개의 그룹으로 나누기 🔗

파티션 내의 행들을 정렬 순서에 따라 n개의 그룹으로 나누고 각 행이 속한 그룹 번호를 반환합니다.
그룹 간의 행 수가 정확히 동일하지 않을 수 있으며, 최대 1개 차이로 분배됩니다.

예시
사용자들의 주문 총액을 기준으로 5분위로 나누고, 각 분위에 속한 사용자 수 세어보기
-- UserTotalAmounts CTE: 사용자별 총 주문 금액 계산
WITH UserTotalAmounts AS (
    SELECT
        user_id,
        SUM(amount) AS total_amount
    FROM Orders
    WHERE user_id IS NOT NULL
    GROUP BY user_id
),
-- UserQuintiles CTE: 각 사용자를 주문 총액 기준 5분위로 나눔
UserQuintiles AS (
    SELECT
        U.name, -- 사용자 이름
        UTA.total_amount, -- 사용자의 총 주문액
        -- 총 주문액을 내림차순으로 정렬하여 5개의 그룹으로 나누고 각 사용자가 속한 그룹 번호 반환
        NTILE(5) OVER (ORDER BY UTA.total_amount DESC) AS amount_quintile
    FROM Users U
    JOIN UserTotalAmounts UTA ON U.id = UTA.user_id -- Users와 UserTotalAmounts 조인
)
-- 메인 쿼리: 각 분위수별 사용자 수 계산
SELECT
    amount_quintile, -- 분위수
    COUNT(*) AS num_users, -- 해당 분위수에 속한 사용자 수
    GROUP_CONCAT(name ORDER BY name SEPARATOR ', ') AS user_list -- (MySQL 전용, Oracle은 LISTAGG) 해당 분위 사용자 목록
FROM UserQuintiles
GROUP BY amount_quintile
ORDER BY amount_quintile;
amount_quintilenum_usersuser_list (MySQL 예시)
11홍길동
21햄랑이
31홍길은
41이동훈
51제임스
(데이터가 적어 각 분위에 1명씩 배정된 예시)

프레임 절: 이동 평균, 누적 합계 정교하게 계산하기 (3편 내용 복습 및 보강) 🔗

윈도우 함수가 계산을 수행할 대상 행의 범위(프레임)를 세밀하게 지정합니다.
윈도우 함수의 PARTITION BYORDER BY 절을 통해 데이터의 특정 범위(프레임)를 지정하여 집계할 수 있습니다.

예시
각 사용자의 주문일자별 주문액과, 최근 3건의 주문에 대한 이동 평균 주문액 계산하기
SELECT
    O.id AS order_id, -- 주문 ID
    U.name AS user_name, -- 사용자 이름
    O.order_date, -- 주문 날짜
    O.amount, -- 현재 주문 금액
    -- 사용자별(PARTITION BY U.id)로 주문 날짜 오름차순(ORDER BY O.order_date) 정렬 후
    -- 현재 행을 포함하여 이전 2개 행 (총 3개 행)의 주문 금액 평균을 계산
    -- 만약 이전 행이 2개 미만이면 있는 만큼만으로 평균 계산
    AVG(O.amount) OVER (PARTITION BY U.id ORDER BY O.order_date
                          ROWS BETWEEN 2 PRECEDING AND CURRENT ROW) AS moving_avg_3_orders
FROM Orders O
JOIN Users U ON O.user_id = U.id -- Users와 Orders 조인
ORDER BY U.name, O.order_date; -- 결과 정렬
order_iduser_nameorder_dateamountmoving_avg_3_orders
2이동훈2023-01-1615001500.0000
7이동훈2023-02-1550003250.0000
...............
1홍길동2023-01-1530003000.0000
3홍길동2023-01-1720002500.0000
6홍길동2023-02-1030002666.6667
10홍길동2023-03-1080004333.3333
(moving_avg_3_orders는 (2000+3000+8000)/3)

🚀

데이터 재구조화 🔗

피벗: 행 데이터를 열로 변환하기 (Pivot) 🔗

행(Row) 형태로 저장된 데이터를 분석하기 용이하도록 열(Column) 형태로 변환합니다.

예시
각 사용자(Users.name)가 주문한 상품(Orders.product)별 총 주문 금액을 상품명을 컬럼으로 하여 표시 (Apple, Banana, Orange만 대상)
nameApple_TotalBanana_TotalOrange_Total
이동훈015000
...

언피벗: 열 데이터를 행으로 변환하기 (Unpivot) 🔗

피벗의 반대 과정으로, 여러 열에 걸쳐 있는 데이터를 특정 속성값에 따라 행으로 변환합니다.
예를 들어, 각 월별 매출액이 별도의 컬럼(Jan_Sales, Feb_Sales 등)으로 있는 테이블을 Month, Sales 컬럼을 가진 행 형태로 변환할 때 사용합니다.

예시
위에서 피벗으로 만든 결과 테이블(또는 유사한 구조의 테이블 PivotedSales)을 다시 원래 형태로 돌리기 (사용자별, 상품별 매출)
nameproductamount
이동훈Banana1500
홍길동Apple6000
홍길동Orange2000

🚀

결론 🔗

네 편에 걸쳐 SQL 조회 문법의 기본부터 심화, 그리고 다양한 고급 활용법까지 함께 알아보았습니다.
오늘 다룬 고급 집계 함수(CUBE, GROUPING SETS), 강력한 문자열 처리를 위한 정규표현식, 윈도우 함수의 깊이 있는 활용법, 그리고 데이터를 유연하게 재구조화하는 피벗팅과 언피벗팅 기법 등은 여러분이 코딩 테스트에서 마주할 수 있는 어떤 SQL 문제라도 자신감을 갖고 해결하는 데 든든한 기반이 될 것입니다.

참고 🔗