홍카나의 공부방

[DuckDB] Python DuckDB 사용법 - 1. 기본 사용법 본문

Data Engineering/Database

[DuckDB] Python DuckDB 사용법 - 1. 기본 사용법

홍문관카페나무 2024. 6. 8. 23:56

DuckDB?

 

DuckDB는 C++로 작성된 오픈소스 컬럼 기반(columnar) 데이터베이스 관리 시스템으로, 인메모리(in-memory)와 OLAP(온라인 분석 쿼리)에 최적화 되어있다는 특징을 보유하고 있다. 표준 SQL을 지원하므로 데이터에 대해 쿼리, 집계, 조인 등의 연산을 수행할 수 있다. SQLite처럼 파일 기반 데이터베이스로, 어플리케이션 내에서 임베디드 되어 실행된다. 별도의 서버 설치가 필요하지 않아 가볍다는 특징이 있다.

 

 

Python에서의 DuckDB API 사용법

DuckDB의 장점은 API를 이용하여 여러 프로그래밍 언어에서 손쉽게 사용할 수 있다는 점이다. 이 글에서는 Python을 통한 DuckDB 사용법을 간단하게 알아보자.

 

먼저 DuckDB를 사용하여 SQL 쿼리를 실행할 수 있다. 간단한 방법은 duckdb.sql 커맨드를 이용하는 것이다.

import duckdb
duckdb.sql("SELECT 42").show()

쿼리 수행 결과

 

위 코드는 파이썬 모듈 내부에 전역적으로 저장된 인메모리 데이터베이스를 사용하여 쿼리를 실행한다. 쿼리의 결과는 릴레이션(Relation)으로 반환되며, 우리가 흔히 아는 행-열 기반 테이블이라고 생각하면 된다. 릴레이션은 변수처럼 저장할 수 있는데, 이렇게 하면 해당 변수를 테이블처럼 사용 가능하고 후속 쿼리에도 사용할 수 있다.

import duckdb
r1 = duckdb.sql("SELECT 42 AS i")
duckdb.sql("SELECT i * 2 AS k FROM r1").show()

쿼리 수행 결과

 

 

데이터 읽기

DuckDB는 여러 데이터 포맷에서 데이터를 읽어들일 수 있다. 자세한 내용은 공식문서의 Data Ingestion을 참고한다. CSV, JSON, Parquet(파케이) 형식을 읽어들일 수 있으며, 디스크와 메모리에서 데이터를 모두 읽을 수 있다.

import duckdb
duckdb.read_csv("example.csv")                # CSV 파일을 릴레이션으로 읽기
duckdb.read_parquet("example.parquet")        # Parquet 파일을 릴레이션으로 읽기
duckdb.read_json("example.json")              # Json 파일을 릴레이션으로 읽기

duckdb.sql("SELECT * FROM 'example.csv'")     # CSV 파일을 즉시 쿼리
duckdb.sql("SELECT * FROM 'example.parquet'") # Parquet 파일을 즉시 쿼리
duckdb.sql("SELECT * FROM 'example.json'")    # Json 파일을 즉시 쿼리

 

 

DataFrames

DuckDB는 Pandas DataFrame를 이용하여 쿼리를 작성할 수도 있다. 또한 Polars, Arrow 테이블도 지원한다. 참고로 read-only(읽기 전용)으로만 가능하며, 해당 테이블에 INSERT나 UPDATE 명령은 사용할 수 없다.

import duckdb

# Pandas DataFrame
import pandas as pd
pandas_df = pd.DataFrame({"a": [42]})
duckdb.sql("SELECT * FROM pandas_df")

# Polars DataFrame
import polars as pl
polars_df = pl.DataFrame({"a": [42]})
duckdb.sql("SELECT * FROM polars_df")

# pyarrow table
import pyarrow as pa
arrow_table = pa.Table.from_pydict({"a": [42]})
duckdb.sql("SELECT * FROM arrow_table")

 

 

결과 변환

DuckDB는 쿼리 결과를 다양한 포맷으로 전환할 수 있다. 예시 코드들은 다음과 같다.

import duckdb
duckdb.sql("SELECT 42").fetchall()   # Python objects
duckdb.sql("SELECT 42").df()         # Pandas DataFrame
duckdb.sql("SELECT 42").pl()         # Polars DataFrame
duckdb.sql("SELECT 42").arrow()      # Arrow Table
duckdb.sql("SELECT 42").fetchnumpy() # NumPy Arrays

 

 

디스크에 데이터 쓰기

릴레이션 객체를 디스크에 다양한 포맷으로 Write할 수 있다. SQL을 이용해서 디스크에 파일을 작성하려면 COPY 명령어를 이용한다.

 

import duckdb
duckdb.sql("SELECT 42").write_parquet("out.parquet") # Write to a Parquet file
duckdb.sql("SELECT 42").write_csv("out.csv")         # Write to a CSV file
duckdb.sql("COPY (SELECT 42) TO 'out.parquet'")      # Copy to a Parquet file

 

 


 

인메모리 데이터베이스 사용

어플리케이션에서 duckdb.connect() 명령어를 이용하면 새로운 DuckDB connection을 사용할 수 있다. duckdb.sql()을 이용해서 DuckDB를 사용하게 되면, 이는 인메모리 데이터베이스 형식으로 작동하게 된다. 즉, 어떠한 테이블도 디스크에 유지되지 않는다. 어떠한 argument 없이 duckdb.connect()를 사용하게 되면 새로운 connection을 반환하게 되며, 인메모리 데이터 베이스 형식을 사용하게 된다.

 

import duckdb

con = duckdb.connect()			# 새로운 DuckDB 커넥션 생성
con.sql("SELECT 42 AS x").show()

 

 

저장소 유지하기

duckdb.connect(dbname) 명령어는 영구적인 데이터베이스를 생성할 수 있다. 해당 커넥션에 작성된 데이터는 계속 유지되며, 해당 파일을 불러오면 파일에 저장된 데이터를 Python 또는 다른 DuckDB 클라이언트에서 재사용할 수 있다. 아래 코드를 직접 실행해보며 파일을 만들어보자.

 

import duckdb

# create a connection to a file called 'file.db'
con = duckdb.connect("file.db")
# create a table and load data into it
con.sql("CREATE TABLE test (i INTEGER)")
con.sql("INSERT INTO test VALUES (42)")
# query the table
con.table("test").show()
# explicitly close the connection
con.close()
# Note: connections also closed implicitly when they go out of scope

 

# .db 파일을 만든다음 아래의 코드를 실행해보자.

import duckdb

with duckdb.connect("file.db") as con:
    con.sql("CREATE TABLE IF NOT EXISTS test (i INTEGER)")
    con.sql("INSERT INTO test VALUES (42)")
    con.table("test").show()
    # the context manager closes the connection automatically

 

 

config 설정하기

duckdb.connect() 파라미터에 config를 딕셔너리 형태로 추가하면, config 옵션을 설정할 수 있다.

import duckdb

con = duckdb.connect(config = {'threads': 1})

 

 

병렬 Connection 사용하기

DuckDBPyConnection 객체는 thread-safe가 보장되지 않는다. 따라서 멀티 스레드를 이용해서 같은 데이터 베이스에 쓰기 작업을 하고 싶다면, 각 스레드마다 DuckDBPyConnection.cursor()를 이용하여 cursor를 만들어줘야 한다.

반응형