explain() 메서드는 DataStore 쿼리의 실행 계획을 보여주며, 어떤 작업이 수행되고 어떤 SQL이 생성되는지 이해하는 데 도움이 됩니다.
기본 사용법
from pathlib import Path
Path("sales.csv").write_text("""\
region,product,category,amount,quantity,price,date,order_id
East,Widget,Electronics,5200,10,120,2024-01-15,1001
West,Gadget,Electronics,800,5,160,2024-02-20,1002
East,Gizmo,Home,6500,3,100,2024-03-10,1003
North,Widget,Electronics,4500,6,150,2024-06-18,1004
West,Gadget,Electronics,2000,8,250,2024-09-14,1005
""")
from chdb import datastore as pd
ds = pd.read_csv("sales.csv")
query = (ds
.filter(ds['amount'] > 1000)
.groupby('region')
.agg({'amount': ['sum', 'mean']})
.sort('sum', ascending=False)
)
# 실행 계획 보기
query.explain()
구문
explain(verbose=False) -> None
| 매개변수 | 유형 | 기본값 | 설명 |
|---|---|---|---|
verbose | bool | False | 추가 메타데이터를 표시함 |
출력 형식
표준 출력
================================================================================
실행 계획 (실행 순서)
================================================================================
[1] 📊 데이터 소스: file('sales.csv', 'csv')
작업:
────────────────────────────────────────────────────────────────────────────────
️ 세그먼트 1 [chDB] (소스에서): 작업 2-5
️ 참고: Pandas 작업 이후의 SQL 작업은 Python() 테이블 함수를 사용합니다
[2] 🚀 [chDB] WHERE: "amount" > 1000
[3] 🚀 [chDB] GROUP BY: region
[4] 🚀 [chDB] AGGREGATE: sum(amount), avg(amount)
[5] 🚀 [chDB] ORDER BY: sum DESC
────────────────────────────────────────────────────────────────────────────────
최종 상태: 📊 대기 중 (지연 실행, 아직 실행되지 않음)
└─> print(), .to_df(), .execute() 호출 시 실행됩니다
────────────────────────────────────────────────────────────────────────────────
생성된 SQL 쿼리:
────────────────────────────────────────────────────────────────────────────────
SELECT region, SUM(amount) AS sum, AVG(amount) AS mean
FROM file('sales.csv', 'csv')
WHERE "amount" > 1000
GROUP BY region
ORDER BY sum DESC
================================================================================
아이콘 범례
| 아이콘 | 의미 |
|---|---|
| 📊 | 데이터 원본 |
| 🚀 | chDB (SQL) 작업 |
| 🐼 | pandas 작업 |
Verbose 출력
query.explain(verbose=True)
세 가지 실행 단계
단계 1: SQL 쿼리 생성(지연)
1. Source: file('sales.csv', 'CSVWithNames')
2. Filter: amount > 1000
3. GroupBy: region
4. Aggregate: sum(amount)
단계 2: 실행 시점
5. Execute SQL -> DataFrame
Trigger: to_df() called
단계 3: DataFrame 연산
6. [pandas] pivot_table(...)
7. [pandas] apply(custom_func)
계획 이해하기
원본 정보
Source: file('sales.csv', 'CSVWithNames')
file()- ClickHouse file() 테이블 함수'CSVWithNames'- 헤더가 포함된 파일 포맷
Source: s3('bucket/data.parquet', ...)
Source: mysql('host', 'db', 'table', ...)
Source: __dataframe__ (pandas DataFrame 입력)
필터 연산
Filter: amount > 1000 AND status = 'active'
GroupBy와 집계
GroupBy: region, category
Aggregate: sum(amount), avg(amount), count(id)
정렬 연산
Sort: sum DESC, region ASC
제한 연산
Limit: 10
Offset: 100
엔진 정보
Filter: amount > 1000
- Engine: chdb
- Pushdown: Yes
Apply: custom_function
- Engine: pandas
- Pushdown: No
Pushdown
- 예: 연산이 데이터 소스(SQL)에서 실행됩니다
- 아니요: 연산을 pandas에서 실행해야 합니다
예시
간단한 쿼리
from pathlib import Path
Path("data.csv").write_text("""\
name,age,city,salary,department
Alice,25,NYC,55000,Engineering
Bob,30,LA,65000,Product
Charlie,35,NYC,80000,Engineering
Diana,28,SF,70000,Design
Eve,42,NYC,95000,Product
""")
ds = pd.read_csv("data.csv")
ds.filter(ds['age'] > 25).explain()
================================================================================
실행 계획 (실행 순서대로)
================================================================================
[1] 📊 데이터 소스: file('data.csv', 'csv')
연산:
────────────────────────────────────────────────────────────────────────────────
️ 세그먼트 1 [chDB] (from source): Operations 2-2
[2] 🚀 [chDB] WHERE: "age" > 25
────────────────────────────────────────────────────────────────────────────────
생성된 SQL 쿼리:
────────────────────────────────────────────────────────────────────────────────
SELECT * FROM file('data.csv', 'csv') WHERE "age" > 25
================================================================================
복합 집계
query = (ds
.filter(ds['date'] >= '2024-01-01')
.filter(ds['amount'] > 100)
.select('region', 'category', 'amount')
.groupby('region', 'category')
.agg({
'amount': ['sum', 'mean', 'count']
})
.sort('sum', ascending=False)
.limit(20)
)
query.explain()
================================================================================
Execution Plan (in execution order)
================================================================================
[1] 📊 데이터 소스: file('sales.csv', 'csv')
Operations:
────────────────────────────────────────────────────────────────────────────────
️ 세그먼트 1 [chDB] (from source): Operations 2-8
[2] 🚀 [chDB] WHERE: "date" >= '2024-01-01'
[3] 🚀 [chDB] WHERE: "amount" > 100
[4] 🚀 [chDB] SELECT: region, category, amount
[5] 🚀 [chDB] GROUP BY: region, category
[6] 🚀 [chDB] AGGREGATE: sum(amount), avg(amount), count(amount)
[7] 🚀 [chDB] ORDER BY: sum DESC
[8] 🚀 [chDB] LIMIT: 20
────────────────────────────────────────────────────────────────────────────────
Generated SQL Query:
────────────────────────────────────────────────────────────────────────────────
SELECT region, category,
SUM(amount) AS sum,
AVG(amount) AS mean,
COUNT(amount) AS count
FROM file('sales.csv', 'csv')
WHERE "date" >= '2024-01-01' AND "amount" > 100
GROUP BY region, category
ORDER BY sum DESC
LIMIT 20
================================================================================
SQL과 pandas 혼용
query = (ds
.filter(ds['age'] > 25) # SQL
.groupby('city') # SQL
.agg({'salary': 'mean'}) # SQL
.apply(lambda x: x * 1.1) # pandas (세그먼트 분할 트리거)
.filter(ds['mean'] > 50000) # SQL (새 세그먼트)
)
query.explain()
================================================================================
실행 계획 (실행 순서대로)
================================================================================
[1] 📊 데이터 소스: file('data.csv', 'csv')
작업:
────────────────────────────────────────────────────────────────────────────────
️ 세그먼트 1 [chDB] (소스에서): 작업 2-4
️ 세그먼트 2 [Pandas] (DataFrame에서): 작업 5
️ 세그먼트 3 [chDB] (DataFrame에서): 작업 6
️ 참고: Pandas 작업 이후의 SQL 작업은 Python() 테이블 함수를 사용합니다
[2] 🚀 [chDB] WHERE: "age" > 25
[3] 🚀 [chDB] GROUP BY: city
[4] 🚀 [chDB] AGGREGATE: avg(salary)
[5] 🐼 [Pandas] APPLY: lambda
[6] 🚀 [chDB] WHERE: "mean" > 50000
================================================================================
explain()으로 디버깅하기
필터 로직 확인하기
# 필터가 올바른지 확인합니다
query = ds.filter((ds['age'] > 25) & (ds['city'] == 'NYC'))
query.explain()
# 출력 결과: Filter: age > 25 AND city = 'NYC'
선택된 컬럼 확인
# 컬럼 프루닝 확인
query = ds.select('name', 'age').filter(ds['age'] > 25)
query.explain()
# 출력 결과: SELECT name, age FROM ... WHERE age > 25
집계 이해하기
# 집계 함수 확인
query = ds.groupby('dept').agg({'salary': ['sum', 'mean', 'std']})
query.explain()
# 출력 결과: SELECT dept, SUM(salary), AVG(salary), stddevPop(salary)
권장 사항
1. 대규모 쿼리 실행 전 확인
# 대용량 데이터의 경우 항상 먼저 explain을 실행하세요
query = ds.complex_pipeline()
query.explain() # 실행 계획 확인
# 실행 계획이 올바르면
result = query.to_df() # 실행
2. 디버깅 시 Verbose 사용
# 문제가 발생한 것 같을 때
query.explain(verbose=True)
# 엔진 선택 및 pushdown 정보를 표시합니다
3. to_sql()과 비교
# explain()은 실행 계획을 보여줍니다
query.explain()
# to_sql()은 SQL만 보여줍니다
print(query.to_sql())
# 각각 다른 목적에 유용합니다
4. Pushdown 상태 확인
# Verbose 모드는 작업이 Pushdown되는지 여부를 표시합니다
query.explain(verbose=True)
# Pushdown: No인 경우, 해당 작업은 pandas에서 실행됩니다
# 성능 향상을 위해 쿼리 구조를 재검토하십시오