레슨 9 / 10·3개 토픽
윈도우 함수와 뷰
순위 함수
윈도우 함수는 현재 행과 관련된 행 집합(윈도우)에 대해 계산을 수행합니다. GROUP BY와 달리 행을 축소하지 않고 각 행에 결과를 추가합니다. OVER() 절로 윈도우를 정의하며, PARTITION BY와 ORDER BY로 범위와 정렬을 지정합니다.
sql
-- ROW_NUMBER: 연속 순번 (중복 없음)
SELECT
name, department, salary,
ROW_NUMBER() OVER (ORDER BY salary DESC) AS row_num
FROM employees;
-- RANK: 동순위 허용, 다음 순위 건너뜀 (1, 2, 2, 4)
SELECT
name, department, salary,
RANK() OVER (ORDER BY salary DESC) AS rank_num
FROM employees;
-- DENSE_RANK: 동순위 허용, 다음 순위 연속 (1, 2, 2, 3)
SELECT
name, department, salary,
DENSE_RANK() OVER (ORDER BY salary DESC) AS dense_rank_num
FROM employees;
-- PARTITION BY: 부서별 순위
SELECT
name, department, salary,
RANK() OVER (
PARTITION BY department
ORDER BY salary DESC
) AS dept_rank
FROM employees;
-- 부서별 TOP 3 추출
WITH ranked AS (
SELECT
name, department, salary,
ROW_NUMBER() OVER (
PARTITION BY department ORDER BY salary DESC
) AS rn
FROM employees
)
SELECT * FROM ranked WHERE rn <= 3;LAG, LEAD, 누적 집계
sql
-- LAG: 이전 행 값 참조 (전월 대비)
SELECT
month,
revenue,
LAG(revenue, 1) OVER (ORDER BY month) AS prev_month,
revenue - LAG(revenue, 1) OVER (ORDER BY month) AS diff,
ROUND(
(revenue - LAG(revenue, 1) OVER (ORDER BY month))
/ LAG(revenue, 1) OVER (ORDER BY month) * 100, 1
) AS growth_pct
FROM monthly_sales;
-- LEAD: 다음 행 값 참조
SELECT
order_date,
customer_id,
LEAD(order_date, 1) OVER (
PARTITION BY customer_id ORDER BY order_date
) AS next_order_date,
DATEDIFF(
LEAD(order_date, 1) OVER (
PARTITION BY customer_id ORDER BY order_date
),
order_date
) AS days_until_next
FROM orders;
-- SUM() OVER: 누적 합계
SELECT
order_date,
amount,
SUM(amount) OVER (ORDER BY order_date) AS running_total,
AVG(amount) OVER (
ORDER BY order_date
ROWS BETWEEN 2 PRECEDING AND CURRENT ROW
) AS moving_avg_3
FROM daily_sales;
-- COUNT, AVG OVER with PARTITION
SELECT
name, department, salary,
COUNT(*) OVER (PARTITION BY department) AS dept_size,
AVG(salary) OVER (PARTITION BY department) AS dept_avg,
salary - AVG(salary) OVER (PARTITION BY department) AS diff_from_avg
FROM employees;VIEW (뷰)
뷰는 저장된 쿼리를 가상 테이블처럼 사용할 수 있게 해줍니다. 복잡한 쿼리를 캡슐화하여 재사용성을 높이고, 보안을 위해 특정 열만 노출하는 데 활용합니다. 단순 뷰는 INSERT/UPDATE/DELETE가 가능합니다.
sql
-- 뷰 생성: 고객 주문 요약
CREATE VIEW customer_order_summary AS
SELECT
u.id AS customer_id,
u.name,
u.email,
COUNT(o.id) AS total_orders,
COALESCE(SUM(o.total_amount), 0) AS total_spent,
MAX(o.ordered_at) AS last_order_date
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.name, u.email;
-- 뷰 사용 (일반 테이블처럼)
SELECT * FROM customer_order_summary
WHERE total_spent > 100000
ORDER BY total_spent DESC;
-- 뷰 수정
CREATE OR REPLACE VIEW customer_order_summary AS
SELECT
u.id AS customer_id,
u.name,
u.email,
COUNT(o.id) AS total_orders,
COALESCE(SUM(o.total_amount), 0) AS total_spent,
MAX(o.ordered_at) AS last_order_date,
DATEDIFF(CURDATE(), MAX(o.ordered_at)) AS days_since_last
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id, u.name, u.email;
-- 갱신 가능한 단순 뷰
CREATE VIEW active_products AS
SELECT id, name, price, stock
FROM products
WHERE is_active = TRUE;
-- 뷰를 통한 UPDATE (단순 뷰는 가능)
UPDATE active_products
SET price = price * 0.9
WHERE stock > 100;
-- 뷰 삭제
DROP VIEW IF EXISTS customer_order_summary;- •
ROW_NUMBER()— 연속 순번 부여 (중복 없음) - •
RANK()— 동순위 허용, 다음 순위 건너뜀 (1,2,2,4) - •
DENSE_RANK()— 동순위 허용, 다음 순위 연속 (1,2,2,3) - •
LAG(col, n)— n개 이전 행의 값 참조 - •
LEAD(col, n)— n개 다음 행의 값 참조 - •
SUM() OVER()— 윈도우 범위 내 누적/이동 집계 - •
PARTITION BY— 윈도우를 그룹으로 분할 - •
CREATE VIEW— 저장된 쿼리를 가상 테이블로 정의
💡
윈도우 함수는 GROUP BY 없이도 집계 결과를 각 행에 표시할 수 있어 매우 강력합니다. 실무에서 "부서별 상위 N명", "전월 대비 증감", "누적 합계" 같은 분석 쿼리에 필수적으로 사용됩니다. 뷰는 JOIN이나 집계가 포함되면 갱신이 불가능하므로 주의하세요.