집계 함수
집계 함수 (Aggregate Functions)
집계 함수는 데이터베이스 전문가가 기대하는 일반적인 방식으로 동작해요. ClickHouse는 여기에 더해 다음도 지원해요.
출처: 문서
본문
집계 함수는 데이터베이스 전문가가 기대하는 일반적인 방식으로 동작해요. ClickHouse는 추가로 다음을 지원해요.
- 파라메트릭 집계 함수 (Parametric aggregate functions) — 컬럼 외에 다른 파라미터도 받는 집계 함수예요.
- 컴비네이터 (Combinators) — 집계 함수의 동작을 바꿔주는 것이에요.
NULL 처리 (NULL processing)
집계 중에는 모든 NULL 인자가 건너뛰어져요. 집계에 여러 인자가 있으면 그중 하나라도 NULL인 행은 모두 무시해요. 이 규칙의 예외는 RESPECT NULLS 수식어가 뒤따르는 first_value, last_value 함수와 그 별칭(any, anyLast)이에요. 예를 들어 FIRST_VALUE(b) RESPECT NULLS처럼요.
예시 (Examples)
다음 테이블을 생각해 보세요.
┌─x─┬────y─┐
│ 1 │ 2 │
│ 2 │ ᴺᵁᴸᴸ │
│ 3 │ 2 │
│ 3 │ 3 │
│ 3 │ ᴺᵁᴸᴸ │
└───┴──────┘
y 컬럼의 값들을 합해야 한다고 해 볼게요.
SELECT sum(y) FROM t_null_big
┌─sum(y)─┐
│ 7 │
└────────┘
이제 groupArray 함수를 사용해 y 컬럼에서 배열을 만들 수 있어요.
SELECT groupArray(y) FROM t_null_big
┌─groupArray(y)─┐
│ [2,2,3] │
└───────────────┘
groupArray는 결과 배열에 NULL을 포함하지 않아요. COALESCE를 사용해 NULL을 사용 사례에 맞는 값으로 바꿀 수 있어요. 예를 들어 avg(COALESCE(column, 0))는 컬럼 값을 집계에 사용하거나, 값이 NULL이면 0을 사용해요.
SELECT
avg(y),
avg(coalesce(y, 0))
FROM t_null_big
┌─────────────avg(y)─┬─avg(coalesce(y, 0))─┐
│ 2.3333333333333335 │ 1.4 │
└────────────────────┴─────────────────────┘
또한 Tuple을 사용해 NULL 건너뛰기 동작을 우회할 수도 있어요. NULL 값만 포함하는 Tuple은 NULL이 아니므로, 집계 함수가 그 NULL 값 때문에 해당 행을 건너뛰지 않아요.
SELECT
groupArray(y),
groupArray(tuple(y)).1
FROM t_null_big;
┌─groupArray(y)─┬─tupleElement(groupArray(tuple(y)), 1)─┐
│ [2,2,3] │ [2,NULL,2,3,NULL] │
└───────────────┴───────────────────────────────────────┘
컬럼이 집계 함수의 인자로 사용될 때는 집계가 건너뛰어진다는 점을 주의하세요. 예를 들어 파라미터가 없는 count() 또는 상수 1을 넣은 count(1)는 블록의 모든 행을 세지만(GROUP BY 컬럼의 값은 인자가 아니므로 무관), count(column)은 column이 NULL이 아닌 행의 수만 반환해요.
SELECT
v,
count(1),
count(v)
FROM
(
SELECT if(number < 10, NULL, number % 3) AS v
FROM numbers(15)
)
GROUP BY v
┌────v─┬─count()─┬─count(v)─┐
│ ᴺᵁᴸᴸ │ 10 │ 0 │
│ 0 │ 1 │ 1 │
│ 1 │ 2 │ 2 │
│ 2 │ 2 │ 2 │
└──────┴─────────┴──────────┘
그리고 여기 RESPECT NULLS를 사용한 first_value 예시가 있어요. NULL 입력이 존중되며, 읽은 첫 값이 NULL이든 아니든 그 값을 반환하는 것을 볼 수 있어요.
SELECT
col || '_' || ((col + 1) * 5 - 1) AS range,
first_value(odd_or_null) AS first,
first_value(odd_or_null) IGNORE NULLS as first_ignore_null,
first_value(odd_or_null) RESPECT NULLS as first_respect_nulls
FROM
(
SELECT
intDiv(number, 5) AS col,
if(number % 2 == 0, NULL, number) AS odd_or_null
FROM numbers(15)
)
GROUP BY col
ORDER BY col
┌─range─┬─first─┬─first_ignore_null─┬─first_respect_nulls─┐
│ 0_4 │ 1 │ 1 │ ᴺᵁᴸᴸ │
│ 1_9 │ 5 │ 5 │ 5 │
│ 2_14 │ 11 │ 11 │ ᴺᵁᴸᴸ │
└───────┴───────┴───────────────────┴─────────────────────┘