Skip to content
ClickHouse Docs
ClickHouse DocsClickHouse Docs

DataStore 쿼리 작성

DataStore는 최적화된 SQL 쿼리로 컴파일되는 SQL 스타일의 쿼리 작성 메서드를 제공합니다. 결과가 필요해질 때까지 모든 작업은 지연 실행됩니다.

쿼리 메서드 개요

메서드 SQL 대응 구문 설명
select(*cols) SELECT cols 컬럼 선택
filter(cond) WHERE cond 행 필터링
where(cond) WHERE cond filter의 별칭
sort(*cols) ORDER BY cols 행 정렬
orderby(*cols) ORDER BY cols sort의 별칭
limit(n) LIMIT n 행 수 제한
offset(n) OFFSET n 행 건너뛰기
distinct() DISTINCT 중복 제거
groupby(*cols) GROUP BY cols 행 그룹화
having(cond) HAVING cond 그룹 필터링
join(right, ...) JOIN DataStore 조인
union(other) UNION 결과 결합

선택

select

DataStore에서 특정 컬럼만 선택합니다.

select(*fields: Union[str, Expression]) -> DataStore

예시:

from chdb.datastore import DataStore
from pathlib import Path
Path("employees.csv").write_text("""\
name,age,city,salary,department,dept_id,status,email,manager_id,bonus
Alice,28,NYC,75000,Engineering,1,active,alice@company.com,3,5000
Bob,35,LA,85000,Engineering,1,active,bob@company.com,3,
Charlie,52,NYC,95000,Product,2,active,charlie@company.com,,10000
Diana,32,SF,70000,Design,3,active,diana@company.com,3,3000
Eve,23,LA,48000,Product,2,inactive,eve@company.com,2,
""")

ds = DataStore.from_file("employees.csv")

# 컬럼 이름으로 선택
result = ds.select('name', 'age', 'salary')

# 모든 컬럼 선택
result = ds.select('*')

# 표현식으로 선택
result = ds.select(
    'name',
    (ds['salary'] * 12).as_('annual_salary'),
    ds['age'].as_('employee_age')
)

# pandas 스타일과 동일
result = ds[['name', 'age', 'salary']]

필터링

filter / where

조건에 따라 행을 필터링합니다. 두 메서드는 동일한 기능을 합니다.

filter(condition) -> DataStore
where(condition) -> DataStore  # alias

예시:

ds = DataStore.from_file("employees.csv")

# 단일 조건
result = ds.filter(ds['age'] > 30)
result = ds.where(ds['salary'] >= 50000)

# 복수 조건 (AND)
result = ds.filter((ds['age'] > 30) & (ds['department'] == 'Engineering'))

# 복수 조건 (OR)
result = ds.filter((ds['city'] == 'NYC') | (ds['city'] == 'LA'))

# NOT 조건
result = ds.filter(~(ds['status'] == 'inactive'))

# 문자열 조건
result = ds.filter(ds['name'].str.contains('John'))
result = ds.filter(ds['email'].str.endswith('@company.com'))

# NULL 검사
result = ds.filter(ds['manager_id'].notnull())
result = ds.filter(ds['bonus'].isnull())

# IN 조건
result = ds.filter(ds['department'].isin(['Engineering', 'Product', 'Design']))

# BETWEEN 조건
result = ds.filter(ds['salary'].between(50000, 100000))

# 연결된 필터 (AND)
result = (ds
    .filter(ds['age'] > 25)
    .filter(ds['salary'] > 50000)
    .filter(ds['city'] == 'NYC')
)

Pandas 스타일 필터링

# 불리언 인덱싱 (filter와 동일)
result = ds[ds['age'] > 30]
result = ds[(ds['age'] > 30) & (ds['salary'] > 50000)]

# 쿼리 메서드
result = ds.query('age > 30 and salary > 50000')

정렬

sort / orderby

하나 이상의 컬럼을 기준으로 행을 정렬합니다.

sort(*fields, ascending=True) -> DataStore
orderby(*fields, ascending=True) -> DataStore  # alias

예시:

ds = DataStore.from_file("employees.csv")

# 단일 컬럼 오름차순
result = ds.sort('name')

# 단일 컬럼 내림차순
result = ds.sort('salary', ascending=False)

# 여러 컬럼
result = ds.sort('department', 'salary')

# 혼합 정렬 순서 (ascending 매개변수에 리스트 사용)
result = ds.sort('department', 'salary', ascending=[True, False])

# Pandas 스타일
result = ds.sort_values('salary', ascending=False)
result = ds.sort_values(['department', 'salary'], ascending=[True, False])

제한 및 페이지네이션

limit

반환할 행 수를 제한합니다.

limit(n: int) -> DataStore

offset

처음 n개 행은 건너뜁니다.

offset(n: int) -> DataStore

예시:

ds = DataStore.from_file("employees.csv")

# 처음 10개 행
result = ds.limit(10)

# 처음 100개 행을 건너뛰고 다음 50개 행을 가져옴
result = ds.offset(100).limit(50)

# Pandas 스타일
result = ds.head(10)
result = ds.tail(10)
result = ds.iloc[100:150]

Distinct

distinct

중복 행을 제거합니다.

distinct(subset=None, keep='first') -> DataStore

예시:

from pathlib import Path
Path("events.csv").write_text("""\
user_id,event_type,timestamp
1,click,2024-01-15 10:30:00
2,view,2024-01-15 11:00:00
1,purchase,2024-01-15 11:30:00
3,click,2024-01-16 09:00:00
2,click,2024-01-16 10:00:00
""")

ds = DataStore.from_file("events.csv")

# 모든 중복 행 제거
result = ds.distinct()

# 특정 컬럼 기준으로 중복 제거
result = ds.distinct(subset=['user_id', 'event_type'])

# Pandas 스타일
result = ds.drop_duplicates()
result = ds.drop_duplicates(subset=['user_id'])

그룹화

groupby

하나 이상의 컬럼을 기준으로 행을 그룹으로 묶습니다. LazyGroupBy 객체를 반환합니다.

groupby(*fields, sort=True, as_index=True, dropna=True) -> LazyGroupBy

예시:

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
""")

ds = DataStore.from_file("sales.csv")

# 단일 컬럼으로 그룹화
by_region = ds.groupby('region')

# 여러 컬럼으로 그룹화
by_region_product = ds.groupby('region', 'product')

# groupby 후 집계
result = ds.groupby('region')['amount'].sum()
result = ds.groupby('region').agg({'amount': 'sum', 'quantity': 'mean'})

# 다중 집계
result = ds.groupby('category').agg({
    'price': ['min', 'max', 'mean'],
    'quantity': 'sum'
})

# 이름 지정 집계
result = ds.groupby('region').agg(
    total_amount=('amount', 'sum'),
    avg_quantity=('quantity', 'mean'),
    order_count=('order_id', 'count')
)

having

집계가 끝난 후 그룹을 필터링합니다.

having(condition: Union[Condition, str]) -> DataStore

예시:

# total > 10000인 그룹 필터링
result = (ds
    .groupby('region')
    .agg({'amount': 'sum'})
    .having(ds['sum'] > 10000)
)

# SQL 스타일 having 사용
result = (ds
    .select('region', 'SUM(amount) as total')
    .groupby('region')
    .having('total > 10000')
)

조인

join

두 DataStore를 조인합니다.

join(right, on=None, how='inner', left_on=None, right_on=None) -> DataStore

매개변수:

매개변수 유형 기본값 설명
right DataStore 필수 조인할 대상인 오른쪽 DataStore
on str/list None 조인 기준으로 사용할 컬럼
how str 'inner' 조인 유형: 'inner', 'left', 'right', 'outer'
left_on str/list None 왼쪽 조인에 사용할 컬럼
right_on str/list None 오른쪽 조인에 사용할 컬럼

예시:

from pathlib import Path
Path("departments.csv").write_text("""\
dept_id,department_name
1,Engineering
2,Product
3,Design
""")

employees = DataStore.from_file("employees.csv")
departments = DataStore.from_file("departments.csv")

# 단일 컬럼에 대한 내부 조인
result = employees.join(departments, on='dept_id')

# 왼쪽 조인
result = employees.join(departments, on='dept_id', how='left')

# 서로 다른 컬럼 이름으로 조인
result = employees.join(
    departments,
    left_on='department_id',
    right_on='id',
    how='inner'
)

# Pandas 스타일 머지
from chdb import datastore as pd
result = pd.merge(employees, departments, on='dept_id')
result = pd.merge(employees, departments, left_on='department_id', right_on='id')

union

두 DataStore의 결과를 합칩니다.

union(other, all=False) -> DataStore

예시:

from pathlib import Path
Path("sales_2023.csv").write_text("""\
region,product,amount,date
East,Widget,1200,2023-06-15
West,Gadget,800,2023-09-20
North,Gizmo,600,2023-11-10
""")
Path("sales_2024.csv").write_text("""\
region,product,amount,date
East,Widget,1500,2024-03-10
North,Gizmo,900,2024-07-22
West,Gadget,1100,2024-05-05
""")

ds1 = DataStore.from_file("sales_2023.csv")
ds2 = DataStore.from_file("sales_2024.csv")

# UNION (중복 제거)
result = ds1.union(ds2)

# UNION ALL (중복 유지)
result = ds1.union(ds2, all=True)

# Pandas 스타일
from chdb import datastore as pd
result = pd.concat([ds1, ds2])

조건 표현식

when

CASE WHEN 표현식을 만듭니다.

when(condition, value) -> CaseWhenBuilder

예시:

ds = DataStore.from_file("employees.csv")

# 단순 case-when
result = ds.select(
    'name',
    ds.when(ds['salary'] > 100000, 'High')
      .when(ds['salary'] > 50000, 'Medium')
      .otherwise('Low')
      .as_('salary_tier')
)

# 컬럼 할당 사용
ds['salary_tier'] = (
    ds.when(ds['salary'] > 100000, 'High')
      .when(ds['salary'] > 50000, 'Medium')
      .otherwise('Low')
)

Raw SQL

run_sql / sql

직접 작성한 SQL 쿼리를 실행합니다.

run_sql(query: str) -> DataStore
sql(query: str) -> DataStore  # alias

예시:

from chdb.datastore import DataStore

# Raw SQL 실행
result = DataStore().sql("""
    SELECT 
        department,
        COUNT(*) as count,
        AVG(salary) as avg_salary
    FROM file('employees.csv', 'CSVWithNames')
    WHERE status = 'active'
    GROUP BY department
    HAVING count > 5
    ORDER BY avg_salary DESC
    LIMIT 10
""")

# 기존 DataStore에서 SQL 실행
ds = DataStore.from_file("employees.csv")
result = ds.sql("SELECT * FROM __table__ WHERE age > 30")

to_sql

실행하지 않고 생성된 SQL을 확인합니다.

to_sql(**kwargs) -> str

예시:

ds = DataStore.from_file("employees.csv")

query = (ds
    .filter(ds['age'] > 30)
    .groupby('department')
    .agg({'salary': 'mean'})
    .sort('mean', ascending=False)
)

print(query.to_sql())
# 출력:
# SELECT department, AVG(salary) AS mean
# FROM file('employees.csv', 'CSVWithNames')
# WHERE age > 30
# GROUP BY department
# ORDER BY mean DESC

메서드 체이닝

모든 쿼리 메서드는 체이닝을 지원합니다:

from chdb.datastore import DataStore

ds = DataStore.from_file("sales.csv")

result = (ds
    .select('region', 'product', 'amount', 'date')
    .filter(ds['date'] >= '2024-01-01')
    .filter(ds['amount'] > 100)
    .groupby('region', 'product')
    .agg({
        'amount': ['sum', 'mean'],
        'date': 'count'
    })
    .having(ds['sum'] > 10000)
    .sort('sum', ascending=False)
    .limit(20)
)

# SQL 보기
print(result.to_sql())

# 실행
df = result.to_df()

별칭

as_

컬럼 또는 서브쿼리의 별칭을 지정합니다.

as_(alias: str) -> DataStore

예시:

# 컬럼 별칭
result = ds.select(
    ds['name'].as_('employee_name'),
    (ds['salary'] * 12).as_('annual_salary')
)

# 서브쿼리 별칭
subquery = ds.filter(ds['age'] > 30).as_('senior_employees')
Navigation