Window Function Frame 경계 — rowsBetween과 rangeBetween의 차이
Apache Spark Window Function Frame 경계 및 PySpark Arrow 최적화 공식 문서 종합 (웹 리서치, 2026) — Apache Spark 공식 문서(pyspark.sql.Window, user_guide/sql/arrow_pandas) + Databricks 블로그 Apache Spark 공식 문서 pyspark.sql.Window.rowsBetween/rangeBetween
이 모듈을 다 읽으면
- rowsBetween과 rangeBetween이 각각 무엇을 기준으로 경계를 정하는지 구분할 수 있다
- ORDER BY를 추가했을 때 기본 프레임이 왜 바뀌는지, 그로 인한 함정을 판단할 수 있다
spark-learn-25에서 다룬 ROW_NUMBER/RANK/LAG/LEAD 같은 윈도우 함수 개념을 넘어, 그 윈도우의 '범위'를 정하는 프레임 경계(rowsBetween/rangeBetween)와 기본 동작의 함정을 정리한다.
rowsBetween — 물리적 행 개수로 경계를 정한다
rowsBetween(start, end)은 현재 행을 기준으로 '몇 번째 행부터 몇 번째 행까지'를 물리적인 행 개수로 센다 — 예를 들어 rowsBetween(-2, 0)은 현재 행 포함 바로 이전 2개 행까지, 총 3개 행을 프레임으로 잡는다. Window.unboundedPreceding(파티션의 첫 행부터)과 Window.currentRow(현재 행까지)를 조합한 rowsBetween(Window.unboundedPreceding, Window.currentRow)은 누적 합계(running total) 계산의 전형적인 패턴이다 — 부서별로 사번 순 급여 누적합을 구하는 식이다.
핵심 포인트
- rowsBetween(start, end)은 현재 행 기준 물리적 행 개수로 경계를 정한다
- rowsBetween(unboundedPreceding, currentRow)은 누적합(running total) 계산의 전형적 패턴
rangeBetween — ORDER BY 값의 '차이'로 경계를 정한다
rangeBetween(start, end)은 행 개수가 아니라 ORDER BY 기준 컬럼의 실제 '값 차이'로 경계를 정한다 — 예를 들어 매출액으로 정렬했을 때 rangeBetween(-100, 0)은 '현재 행의 매출액보다 100 이하로 작은 값을 가진 모든 행'을 프레임에 포함시킨다(몇 개 행이 포함될지는 실제 값 분포에 따라 달라진다). 이 차이 때문에 rowsBetween은 '항상 정확히 N개 행'을 보장하지만, rangeBetween은 '값이 특정 범위 안에 있는 행 전부'를 보장한다 — 같은 정렬 값을 가진 행(동점, tie)이 여러 개 있을 때 이 차이가 두드러진다: rowsBetween은 동점이어도 딱 지정한 개수만 세지만, rangeBetween은 동점인 행 전부를 프레임에 포함시킨다.
핵심 포인트
- rangeBetween(start, end)은 행 개수가 아니라 ORDER BY 컬럼의 실제 값 차이로 경계를 정한다
- rowsBetween은 항상 정확히 N개 행 보장, rangeBetween은 동점(tie) 행을 전부 포함시킬 수 있어 개수가 달라질 수 있다
함정 — ORDER BY를 추가하는 순간 기본 프레임이 바뀐다
프레임을 명시적으로 지정하지 않으면 기본 동작이 있다: ORDER BY가 없는 윈도우는 기본적으로 파티션 전체(unbounded)를 프레임으로 잡지만, ORDER BY가 있는 윈도우는 기본적으로 '성장하는 프레임'(unboundedPreceding, currentRow) — 즉 암묵적인 누적 계산 프레임을 쓴다. 이 때문에 단순히 순위를 매기려고 ORDER BY만 추가했는데, 그 위에 SUM() 같은 집계를 함께 쓰면 의도치 않게 '전체 합계'가 아니라 '누적 합계'가 나오는 흔한 실수가 생긴다 — SUM(col) OVER (PARTITION BY p ORDER BY o)가 전체 합이 아니라 순번별 누적합을 반환하는 이유가 바로 이 기본 프레임 변경 때문이다. 의도한 계산이 명확하면 rowsBetween/rangeBetween을 명시적으로 지정해 이 암묵적 기본값에 의존하지 않는 것이 안전하다.
핵심 포인트
- ORDER BY 없는 윈도우는 기본이 파티션 전체(unbounded), ORDER BY 있는 윈도우는 기본이 '성장하는 프레임'(누적)
- SUM() OVER (... ORDER BY ...)가 전체합이 아니라 누적합을 내는 흔한 실수의 원인이 이 기본 프레임 전환
- 의도한 계산이 명확하면 프레임을 명시적으로 지정해 암묵적 기본값에 의존하지 않는 게 안전하다