동적 컬럼 선택
동적 컬럼 선택
동적 컬럼 선택은 강력하지만 잘 쓰이지 않는 ClickHouse 기능으로, 각 컬럼을 개별적으로 이름 붙이는 대신 정규 표현식으로 컬럼을 선택할 수 있게 해줘요. APPLY 수정자를 사용해 일치하는 컬럼에 함수를 적용할 수도 있어서, 데이터 분석과 변환 작업에 매우 유용해요. 이 기능을 New York taxis 데이터셋으로 배워볼게요. 이 데이터셋은 ClickHouse SQL playground에서도 찾을 수 있어요.
출처: 문서
본문
패턴과 일치하는 컬럼 선택하기
흔한 시나리오부터 시작해볼게요: NYC taxi 데이터셋에서 _amount를 포함하는 컬럼만 선택하기. 각 컬럼 이름을 수동으로 입력하는 대신 정규 표현식과 함께 COLUMNS 표현식을 사용할 수 있어요:
FROM nyc_taxi.trips
SELECT COLUMNS('.*_amount')
LIMIT 10;
이 쿼리는 첫 10행을 반환하지만, 이름이 패턴 .*_amount("_amount" 뒤에 임의의 문자가 오는)와 일치하는 컬럼에 대해서만 반환해요.
┌─fare_amount─┬─tip_amount─┬─tolls_amount─┬─total_amount─┐
1. │ 9 │ 0 │ 0 │ 9.8 │
2. │ 9 │ 0 │ 0 │ 9.8 │
3. │ 3.5 │ 0 │ 0 │ 4.8 │
4. │ 3.5 │ 0 │ 0 │ 4.8 │
5. │ 3.5 │ 0 │ 0 │ 4.3 │
6. │ 3.5 │ 0 │ 0 │ 4.3 │
7. │ 2.5 │ 0 │ 0 │ 3.8 │
8. │ 2.5 │ 0 │ 0 │ 3.8 │
9. │ 5 │ 0 │ 0 │ 5.8 │
10. │ 5 │ 0 │ 0 │ 5.8 │
└─────────────┴────────────┴──────────────┴──────────────┘
fee나 tax 항목을 포함하는 컬럼도 반환하고 싶다고 해볼게요. 정규 표현식을 업데이트해 포함시킬 수 있어요:
SELECT COLUMNS('.*_amount|fee|tax')
FROM nyc_taxi.trips
ORDER BY rand()
LIMIT 3;
┌─fare_amount─┬─mta_tax─┬─tip_amount─┬─tolls_amount─┬─ehail_fee─┬─total_amount─┐
1. │ 5 │ 0.5 │ 1 │ 0 │ 0 │ 7.8 │
2. │ 12.5 │ 0.5 │ 0 │ 0 │ 0 │ 13.8 │
3. │ 4.5 │ 0.5 │ 1.66 │ 0 │ 0 │ 9.96 │
└─────────────┴─────────┴────────────┴──────────────┴───────────┴──────────────┘
여러 패턴 선택하기
단일 쿼리에서 여러 컬럼 패턴을 결합할 수 있어요:
SELECT
COLUMNS('.*_amount'),
COLUMNS('.*_date.*')
FROM nyc_taxi.trips
LIMIT 5;
┌─fare_amount─┬─tip_amount─┬─tolls_amount─┬─total_amount─┬─pickup_date─┬─────pickup_datetime─┬─dropoff_date─┬────dropoff_datetime─┐
1. │ 9 │ 0 │ 0 │ 9.8 │ 2001-01-01 │ 2001-01-01 00:01:48 │ 2001-01-01 │ 2001-01-01 00:15:47 │
2. │ 9 │ 0 │ 0 │ 9.8 │ 2001-01-01 │ 2001-01-01 00:01:48 │ 2001-01-01 │ 2001-01-01 00:15:47 │
3. │ 3.5 │ 0 │ 0 │ 4.8 │ 2001-01-01 │ 2001-01-01 00:02:08 │ 2001-01-01 │ 2001-01-01 01:00:02 │
4. │ 3.5 │ 0 │ 0 │ 4.8 │ 2001-01-01 │ 2001-01-01 00:02:08 │ 2001-01-01 │ 2001-01-01 01:00:02 │
5. │ 3.5 │ 0 │ 0 │ 4.3 │ 2001-01-01 │ 2001-01-01 00:02:26 │ 2001-01-01 │ 2001-01-01 00:04:49 │
└─────────────┴────────────┴──────────────┴──────────────┴─────────────┴─────────────────────┴──────────────┴─────────────────────┘
모든 컬럼에 함수 적용하기
APPLY 수정자를 사용해 모든 컬럼에 걸쳐 함수를 적용할 수도 있어요. 예를 들어 그 각 컬럼의 최댓값을 찾고 싶다면 다음 쿼리를 실행할 수 있어요:
SELECT COLUMNS('.*_amount|fee|tax') APPLY(max)
FROM nyc_taxi.trips;
┌─max(fare_amount)─┬─max(mta_tax)─┬─max(tip_amount)─┬─max(tolls_amount)─┬─max(ehail_fee)─┬─max(total_amount)─┐
1. │ 998310 │ 500000.5 │ 3950588.8 │ 7999.92 │ 1.95 │ 3950611.5 │
└──────────────────┴──────────────┴─────────────────┴───────────────────┴────────────────┴───────────────────┘
어쩌면 평균을 보고 싶을 수도 있어요:
SELECT COLUMNS('.*_amount|fee|tax') APPLY(avg)
FROM nyc_taxi.trips
┌─avg(fare_amount)─┬───────avg(mta_tax)─┬────avg(tip_amount)─┬──avg(tolls_amount)─┬──────avg(ehail_fee)─┬──avg(total_amount)─┐
1. │ 11.8044154834777 │ 0.4555942672733423 │ 1.3469850969211845 │ 0.2256511991414463 │ 3.37600560437412e-9 │ 14.423323722271563 │
└──────────────────┴────────────────────┴────────────────────┴────────────────────┴─────────────────────┴────────────────────┘
그 값들에는 소수점 자리가 많지만, 다행히 함수를 체이닝해서 고칠 수 있어요. 이 경우 avg 함수 다음에 round 함수를 적용할게요:
SELECT COLUMNS('.*_amount|fee|tax') APPLY(avg) APPLY(round)
FROM nyc_taxi.trips;
┌─round(avg(fare_amount))─┬─round(avg(mta_tax))─┬─round(avg(tip_amount))─┬─round(avg(tolls_amount))─┬─round(avg(ehail_fee))─┬─round(avg(total_amount))─┐
1. │ 12 │ 0 │ 1 │ 0 │ 0 │ 14 │
└─────────────────────────┴─────────────────────┴────────────────────────┴──────────────────────────┴───────────────────────┴──────────────────────────┘
하지만 이렇게 하면 평균이 정수로 반올림돼요. 예를 들어 소수점 2자리로 반올림하고 싶다면 그렇게 할 수도 있어요. APPLY 수정자는 함수를 받는 것 외에도 람다를 받는데, 이 덕분에 round 함수가 평균 값을 소수점 2자리로 반올림하도록 유연하게 할 수 있어요:
SELECT COLUMNS('.*_amount|fee|tax') APPLY(avg) APPLY(x -> round(x, 2))
FROM nyc_taxi.trips;
┌─round(avg(fare_amount), 2)─┬─round(avg(mta_tax), 2)─┬─round(avg(tip_amount), 2)─┬─round(avg(tolls_amount), 2)─┬─round(avg(ehail_fee), 2)─┬─round(avg(total_amount), 2)─┐
1. │ 11.8 │ 0.46 │ 1.35 │ 0.23 │ 0 │ 14.42 │
└────────────────────────────┴────────────────────────┴───────────────────────────┴─────────────────────────────┴──────────────────────────┴─────────────────────────────┘
컬럼 교체하기
지금까지는 잘 됐어요. 하지만 다른 값들은 그대로 두고 하나의 값만 조정하고 싶다고 해볼게요. 예를 들어 total amount를 두 배로 하고 MTA tax를 1.1로 나누고 싶을 수 있어요. 이는 다른 컬럼은 그대로 두면서 컬럼을 교체하는 REPLACE 수정자를 사용해서 할 수 있어요.
FROM nyc_taxi.trips
SELECT
COLUMNS('.*_amount|fee|tax')
REPLACE(
total_amount*2 AS total_amount,
mta_tax/1.1 AS mta_tax
)
APPLY(avg)
APPLY(col -> round(col, 2));
┌─round(avg(fare_amount), 2)─┬─round(avg(di⋯, 1.1)), 2)─┬─round(avg(tip_amount), 2)─┬─round(avg(tolls_amount), 2)─┬─round(avg(ehail_fee), 2)─┬─round(avg(mu⋯nt, 2)), 2)─┐
1. │ 11.8 │ 0.41 │ 1.35 │ 0.23 │ 0 │ 28.85 │
└────────────────────────────┴──────────────────────────┴───────────────────────────┴─────────────────────────────┴──────────────────────────┴──────────────────────────┘
컬럼 제외하기
EXCEPT 수정자를 사용해 필드를 제외할 수도 있어요. 예를 들어 tolls_amount 컬럼을 제거하려면 다음 쿼리를 작성할 수 있어요:
FROM nyc_taxi.trips
SELECT
COLUMNS('.*_amount|fee|tax') EXCEPT(tolls_amount)
REPLACE(
total_amount*2 AS total_amount,
mta_tax/1.1 AS mta_tax
)
APPLY(avg)
APPLY(col -> round(col, 2));
┌─round(avg(fare_amount), 2)─┬─round(avg(di⋯, 1.1)), 2)─┬─round(avg(tip_amount), 2)─┬─round(avg(ehail_fee), 2)─┬─round(avg(mu⋯nt, 2)), 2)─┐
1. │ 11.8 │ 0.41 │ 1.35 │ 0 │ 28.85 │
└────────────────────────────┴──────────────────────────┴───────────────────────────┴──────────────────────────┴──────────────────────────┘