MySQL 8.0부터 본격적으로 지원되기 시작한 윈도우 함수(Window Functions)는 복잡한 분석 쿼리를 간결하게 작성할 수 있게 해주는 강력한 기능입니다. 하지만 대용량 데이터베이스에서 윈도우 함수 사용 시 성능 저하가 발생하는 경우가 많습니다. 이 글에서는 윈도우 함수와 인덱스의 관계를 탐구하고, 성능 최적화 방안을 알아보겠습니다.
윈도우 함수는 행과 행 사이의 관계를 정의하여 계산을 수행하는 함수입니다. PARTITION BY 절을 사용한 윈도우 함수는 특정 컬럼을 기준으로 데이터를 논리적 파티션으로 나누어 처리합니다.
SELECT *,
ROW_NUMBER() OVER (PARTITION BY aptCode ORDER BY dealYear DESC, dealMonth DESC, dealDay DESC) AS recentDeal
FROM housedeal
이 예제에서 ROW_NUMBER()는 각 aptCode 별로 따로 번호를 매기며, 날짜 내림차순으로 정렬합니다.
인덱스는 데이터베이스 쿼리 성능을 향상시키는 데이터 구조입니다. 인덱스는 주로 다음 작업에서 성능 향상을 제공합니다:
MySQL에서 쿼리 실행 순서는 다음과 같습니다:
중요: 윈도우 함수는 SELECT 이후, ORDER BY 이전에 적용됩니다.
윈도우 함수가 인덱스를 활용하지 못하는 이유는 여러 가지가 있습니다:
실행 순서 문제:
처리 방식 차이:
임시 테이블 사용:
다음 쿼리를 통해 실제 사례를 분석해 보겠습니다:
SELECT count(*)
FROM (SELECT *,
ROW_NUMBER() OVER (PARTITION BY aptCode ORDER BY dealYear DESC, dealMonth DESC, dealDay DESC) AS recentDeal
FROM housedeal
NATURAL JOIN (SELECT * FROM houseinfo WHERE dongCode like '11%' ) AS houseinfo) AS deals
WHERE recentDeal = 1 ORDER BY dealYear desc, dealMonth desc, dealDay desc;
EXPLAIN 결과 분석:
1 PRIMARY <derived2> ref <auto_key0> <auto_key0> 8 const 10 100.00
2 DERIVED houseinfo ALL PRIMARY 41820 11.11 Using where; Using temporary; Using filesort
2 DERIVED housedeal ref housedeal_aptCode_houseinfo_aptCode_fk_idx,idx_housedeal_apt_date idx_housedeal_apt_date 9 whereismyhome.houseinfo.aptCode 9310 0.00
문제점:
houseinfo 테이블의 전체 스캔 (type=ALL)인덱스 사용 제한:
WHERE dongCode like '11%'에 인덱스가 사용될 수 있지만ROW_NUMBER() OVER (...)와 WHERE recentDeal = 1에는 인덱스를 활용할 수 없음SELECT COUNT(*)
FROM housedeal hd
INNER JOIN houseinfo hi ON hd.aptCode = hi.aptCode
INNER JOIN (
SELECT h.aptCode, MAX(CONCAT(h.dealYear, LPAD(h.dealMonth, 2, '0'), LPAD(h.dealDay, 2, '0'))) as max_date
FROM housedeal h
INNER JOIN houseinfo i ON h.aptCode = i.aptCode
WHERE i.dongCode LIKE '11%'
GROUP BY h.aptCode
) latest ON hd.aptCode = latest.aptCode
AND CONCAT(hd.dealYear, LPAD(hd.dealMonth, 2, '0'), LPAD(hd.dealDay, 2, '0')) = latest.max_date
WHERE hi.dongCode LIKE '11%';
장점:
-- 1. 서울 지역 아파트 코드 추출
CREATE TEMPORARY TABLE seoul_apts AS
SELECT aptCode FROM houseinfo WHERE dongCode LIKE '11%';
-- 2. 인덱스 생성
ALTER TABLE seoul_apts ADD INDEX (aptCode);
-- 3. 각 아파트별 최신 거래 날짜 계산
CREATE TEMPORARY TABLE apt_max_dates AS
SELECT
hd.aptCode,
MAX(CONCAT(hd.dealYear, LPAD(hd.dealMonth, 2, '0'), LPAD(hd.dealDay, 2, '0'))) as max_date
FROM
housedeal hd
INNER JOIN seoul_apts sa ON hd.aptCode = sa.aptCode
GROUP BY
hd.aptCode;
-- 4. 인덱스 생성
ALTER TABLE apt_max_dates ADD INDEX (aptCode, max_date);
-- 5. 각 아파트별 최신 거래 중 하나만 선택
CREATE TEMPORARY TABLE latest_deals AS
SELECT
hd.aptCode,
MIN(hd.no) AS deal_id
FROM
housedeal hd
INNER JOIN apt_max_dates amd ON hd.aptCode = amd.aptCode
AND CONCAT(hd.dealYear, LPAD(hd.dealMonth, 2, '0'), LPAD(hd.dealDay, 2, '0')) = amd.max_date
GROUP BY
hd.aptCode;
-- 6. 결과 카운트
SELECT COUNT(*) FROM latest_deals;
장점:
-- 인덱스 최적화
CREATE INDEX idx_houseinfo_dongcode ON houseinfo(dongCode);
CREATE INDEX idx_housedeal_apt_date ON housedeal(aptCode, dealYear DESC, dealMonth DESC, dealDay DESC);
윈도우 함수는 인덱스를 직접 활용하지 않습니다
대안 접근법
선택 기준
윈도우 함수는 강력한 기능이지만 인덱스 활용에 제한이 있기 때문에, 상황에 맞는 최적화 전략을 선택하는 것이 중요합니다.