FILTER 절

FILTER 절 (FILTER Clause)

FILTER 절은 SELECT 문에서 집계 함수 뒤에 선택적으로 붙을 수 있어요. WHERE 절이 행을 필터링하는 것처럼, 집계 함수로 들어가는 데이터 행을 필터링하되 특정 집계 함수에 국한해서 적용할 수 있어요. 피벗(pivot) 작업을 할 때 특히 깔끔한 문법을 제공한답니다.

출처: 문서

본문

FILTER 절은 SELECT 문에서 집계 함수 뒤에 선택적으로 붙을 수 있어요. 이는 WHERE 절이 행을 필터링하는 것과 같은 방식으로 집계 함수로 들어가는 데이터 행을 필터링하되, 특정 집계 함수에 국한해서 적용해요.

서로 다른 필터로 여러 집계를 평가하거나 데이터셋의 피벗 뷰를 만들 때 등 여러 상황에서 유용해요. FILTER는 아래에서 다루는 더 전통적인 CASE WHEN 접근 방식과 비교할 때 데이터 피벗에 더 깔끔한 문법을 제공해요.

또한 일부 집계 함수는 NULL 값을 필터링하지 않기 때문에, CASE WHEN 접근 방식으로는 올바른 결과가 나오지 않을 때도 FILTER 절을 쓰면 유효한 결과를 얻을 수 있어요. 이는 firstlast 함수에서 발생하는데, 단순히 데이터를 재집계하지 않고 컬럼으로 재배향(re-orient)하려는 비집계 피벗 연산에서 바람직해요. FILTER는 또한 listarray_agg 함수를 사용할 때 NULL 처리를 개선해요 — CASE WHEN 접근 방식은 목록 결과에 NULL 값을 포함하는 반면, FILTER 절은 이를 제거하기 때문이에요.

예시 (Examples)

다음을 반환해 보세요:

  • 전체 행 수
  • i <= 5인 행 수
  • i가 홀수인 행 수
SELECT
    count() AS total_rows,
    count() FILTER (i <= 5) AS lte_five,
    count() FILTER (i % 2 = 1) AS odds
FROM generate_series(1, 10) tbl(i);
total_rows lte_five odds
10 5 5

조건을 만족하는 행을 단순히 세는 것은 FILTER 절 없이도 불리언 sum 집계 함수로 가능해요. 예: sum(i <= 5).

서로 다른 집계 함수를 사용할 수 있고, 여러 WHERE 표현식도 허용돼요:

SELECT
    sum(i) FILTER (i <= 5) AS lte_five_sum,
    median(i) FILTER (i % 2 = 1) AS odds_median,
    median(i) FILTER (i % 2 = 1 AND i <= 5) AS odds_lte_five_median
FROM generate_series(1, 10) tbl(i);
lte_five_sum odds_median odds_lte_five_median
15 5.0 3.0

FILTER 절은 데이터를 행에서 컬럼으로 피벗하는 데도 사용할 수 있어요. 이는 정적 피벗이에요 — SQL에서 컬럼이 런타임 전에 정의되어야 하기 때문이에요. 하지만 이런 종류의 문은 호스트 프로그래밍 언어에서 동적으로 생성해 DuckDB의 SQL 엔진을 활용해 메모리보다 큰 데이터를 빠르게 피벗할 수 있어요.

먼저 예제 데이터셋을 생성해 보세요:

CREATE TEMP TABLE stacked_data AS
    SELECT
        i,
        CASE WHEN i <= rows * 0.25  THEN 2022
             WHEN i <= rows * 0.5   THEN 2023
             WHEN i <= rows * 0.75  THEN 2024
             WHEN i <= rows * 0.875 THEN 2025
             ELSE NULL
             END AS year
    FROM (
        SELECT
            i,
            count(*) OVER () AS rows
        FROM generate_series(1, 100_000_000) tbl(i)
    ) tbl;

데이터를 연도별로 "피벗"(각 연도를 별도 컬럼으로 이동)해 보세요:

SELECT
    count(i) FILTER (year = 2022) AS "2022",
    count(i) FILTER (year = 2023) AS "2023",
    count(i) FILTER (year = 2024) AS "2024",
    count(i) FILTER (year = 2025) AS "2025",
    count(i) FILTER (year IS NULL) AS "NULLs"
FROM stacked_data;

이 문법은 위의 FILTER 절과 같은 결과를 만들어요:

SELECT
    count(CASE WHEN year = 2022 THEN i END) AS "2022",
    count(CASE WHEN year = 2023 THEN i END) AS "2023",
    count(CASE WHEN year = 2024 THEN i END) AS "2024",
    count(CASE WHEN year = 2025 THEN i END) AS "2025",
    count(CASE WHEN year IS NULL THEN i END) AS "NULLs"
FROM stacked_data;
2022 2023 2024 2025 NULLs
25000000 25000000 25000000 12500000 12500000

하지만 CASE WHEN 접근 방식은 NULL 값을 무시하지 않는 집계 함수를 사용할 때는 예상대로 작동하지 않아요. first 함수가 이 범주에 속하므로, 이 경우 FILTER가 선호돼요.

데이터를 연도별로 "피벗"(각 연도를 별도 컬럼으로 이동)해 보세요:

SELECT
    first(i) FILTER (year = 2022) AS "2022",
    first(i) FILTER (year = 2023) AS "2023",
    first(i) FILTER (year = 2024) AS "2024",
    first(i) FILTER (year = 2025) AS "2025",
    first(i) FILTER (year IS NULL) AS "NULLs"
FROM stacked_data;
2022 2023 2024 2025 NULLs
1474561 25804801 50749441 76431361 87500001

CASE WHEN 절의 첫 평가가 NULL을 반환할 때마다 NULL 값을 만들어내요:

SELECT
    first(CASE WHEN year = 2022 THEN i END) AS "2022",
    first(CASE WHEN year = 2023 THEN i END) AS "2023",
    first(CASE WHEN year = 2024 THEN i END) AS "2024",
    first(CASE WHEN year = 2025 THEN i END) AS "2025",
    first(CASE WHEN year IS NULL THEN i END) AS "NULLs"
FROM stacked_data;
2022 2023 2024 2025 NULLs
1228801 NULL NULL NULL NULL

집계 함수 문법 (FILTER 절 포함) (Aggregate Function Syntax)

더 알아보기 (Learn more)