DuckDB는 데이터를 복사하지 않고도 Apache Arrow 테이블을 직접 쿼리할 수 있는 프로세스 중인 SQL 분석 엔진입니다. DuckDB와 mssql-python 드라이버를 결합하면 다음과 같은 기능이 가능합니다:
- 판다스나 폴라에 데이터를 로드하지 않고 Microsoft SQL 결과 세트에 대해 분석 SQL 쿼리를 실행하세요.
- 0 복사 오버헤드가 있는 메모리 내 Arrow 테이블을 쿼리합니다.
- Microsoft SQL 데이터와 로컬 파일(CSV, Parquet, JSON)을 단일 DuckDB 쿼리에서 결합할 수 있습니다.
- DuckDB를 통해 Microsoft SQL 데이터를 Parquet, CSV 또는 기타 형식으로 내보내세요.
사전 요구 사항
- Python 3.10 이상.
-
mssql-python, ,duckdb그리고pyarrow패키지들.pip install mssql-python duckdb pyarrow로 모두 설치하세요. - 일회성 운영 체제 관련 필수 구성 요소를 설치합니다. Windows 사용자는 이 단계를 건너뛸 수 있습니다. 전체 플랫폼 세부사항은 Install mssql-python을 참조하세요.
SQL 데이터베이스 만들기
다음 플랫폼 중 하나에서 SQL 데이터베이스를 생성하거나 연결하세요:
이 글의 예시들은 샘플 데이터베이스를 조회합니다 AdventureWorks . 아직 가지고 있지 않다면, AdventureWorks 샘플 데이터베이스를 참고하세요.
종속성 설치
pip install mssql-python duckdb pyarrow
DuckDB로 Microsoft SQL 데이터를 조회
기본 워크플로우는 이렇습니다: mssql-python으로 쿼리를 실행하고, 결과를 Arrow 테이블로 가져오고, 그 Arrow 테이블을 DuckDB SQL로 쿼리합니다.
기본 패턴
먼저 연결을 설정하고 Arrow 테이블로 데이터를 가져오세요.
import duckdb
import mssql_python
conn = mssql_python.connect(
"Server=<server>.database.windows.net;"
"Database=<database>;"
"Authentication=ActiveDirectoryDefault;"
"Encrypt=yes"
)
cursor = conn.cursor()
# Fetch Microsoft SQL data as Arrow
cursor.execute("SELECT * FROM Production.Product WHERE ListPrice > 0")
products = cursor.arrow()
# Query the Arrow table with DuckDB
result = duckdb.sql("""
SELECT Color, COUNT(*) AS ProductCount, AVG(ListPrice) AS AvgPrice
FROM products
GROUP BY Color
ORDER BY ProductCount DESC
""")
print(result.fetchdf())
DuckDB는 Arrow 테이블을 products Python 변수 이름으로 참조합니다. DuckDB 저장소에는 데이터가 복사되지 않습니다.
집합체와 필터
DuckDB의 SQL을 사용해 Arrow 데이터를 그룹화하고 집계하세요.
cursor.execute("SELECT * FROM Sales.SalesOrderHeader")
orders = cursor.arrow()
# Top customers by total spend
top_customers = duckdb.sql("""
SELECT
CustomerID,
COUNT(*) AS OrderCount,
SUM(TotalDue) AS TotalSpent,
AVG(TotalDue) AS AvgOrderValue
FROM orders
GROUP BY CustomerID
HAVING SUM(TotalDue) > 10000
ORDER BY TotalSpent DESC
LIMIT 20
""")
print(top_customers.fetchdf())
여러 Microsoft SQL 결과에 참여하기
Microsoft SQL에서 여러 테이블을 가져와서 DuckDB에 크로스 서버 쿼리를 작성하지 않고 합치세요.
# Fetch two tables
cursor.execute("SELECT * FROM Production.Product")
products = cursor.arrow()
cursor.execute("SELECT * FROM Production.ProductSubcategory")
subcategories = cursor.arrow()
# Join in DuckDB
result = duckdb.sql("""
SELECT
s.Name AS Subcategory,
COUNT(*) AS ProductCount,
ROUND(AVG(p.ListPrice), 2) AS AvgPrice
FROM products p
JOIN subcategories s ON p.ProductSubcategoryID = s.ProductSubcategoryID
GROUP BY s.Name
ORDER BY AvgPrice DESC
""")
print(result.fetchdf())
Microsoft SQL 데이터를 로컬 파일과 결합하세요
DuckDB는 CSV, Parquet, JSON 파일을 네이티브로 읽을 수 있습니다. SQL Server 데이터와 로컬 파일을 단일 쿼리에서 결합하세요.
CSV 파일로 조인하세요
CSV 파일을 불러와서 Microsoft SQL의 데이터와 연결하세요.
import csv
from pathlib import Path
cursor.execute("SELECT CustomerID, PersonID FROM Sales.Customer")
customers = cursor.arrow()
csv_path = Path("customer_regions.csv")
with csv_path.open("w", newline="", encoding="utf-8") as file:
writer = csv.writer(file)
writer.writerow(["CustomerID", "Region", "Segment"])
writer.writerows([
(1, "West", "Premium"),
(2, "East", "Standard"),
(3, "Central", "Basic"),
])
try:
result = duckdb.sql("""
SELECT c.CustomerID, c.PersonID, f.Region, f.Segment
FROM customers c
JOIN read_csv_auto('customer_regions.csv') f ON c.CustomerID = f.CustomerID
""")
print(result.fetchdf())
finally:
csv_path.unlink(missing_ok=True)
Parquet 파일로 조인
Parquet 파일을 불러와서 Microsoft SQL 데이터와 결합하세요.
from pathlib import Path
import pyarrow as pa
import pyarrow.parquet as pq
cursor.execute("SELECT ProductID, Name, ListPrice FROM Production.Product")
products = cursor.arrow()
parquet_path = Path("order_history.parquet")
order_history = pa.table({
"ProductID": [1, 2, 680],
"OrderDate": ["2024-06-01", "2024-03-15", "2024-01-10"],
"Quantity": [10, 5, 3],
})
pq.write_table(order_history, parquet_path)
try:
result = duckdb.sql("""
SELECT p.Name, p.ListPrice, h.OrderDate, h.Quantity
FROM products p
JOIN read_parquet('order_history.parquet') h ON p.ProductID = h.ProductID
WHERE h.OrderDate >= '2024-01-01'
""")
print(result.fetchdf())
finally:
parquet_path.unlink(missing_ok=True)
Microsoft SQL 데이터를 내보내기
DuckDB의 문장을 COPY 사용해 Microsoft SQL 데이터를 다양한 파일 형식으로 내보내세요.
Parquet로 내보내기
데이터를 Apache Parquet 형식으로 내보내세요.
cursor.execute("SELECT * FROM Production.Product")
products = cursor.arrow()
duckdb.sql("COPY products TO 'products.parquet' (FORMAT PARQUET)")
CSV로 내보내기
쉼표 구분된 값 파일로 데이터를 내보내기:
cursor.execute("SELECT * FROM Sales.SalesOrderHeader")
orders = cursor.arrow()
duckdb.sql("COPY orders TO 'orders.csv' (FORMAT CSV, HEADER)")
파티션된 Parquet 내보내기
분산 분석을 위해 Parquet 파티션된 파일로 데이터를 내보내기:
import shutil
from pathlib import Path
cursor.execute("SELECT * FROM Sales.SalesOrderHeader")
orders = cursor.arrow()
output_dir = Path("sales_data")
shutil.rmtree(output_dir, ignore_errors=True)
duckdb.sql("""
COPY (SELECT *, YEAR(OrderDate) AS OrderYear FROM orders)
TO 'sales_data'
(FORMAT PARQUET, PARTITION_BY (OrderYear))
""")
대용량 결과 집합 스트리밍
대규모 데이터셋의 경우, 모든 행을 한 번에 메모리에 로드하지 않고 스트리밍 배치로 데이터를 처리하는 방법을 사용 arrow_reader() 하세요:
cursor.execute("SELECT * FROM Production.TransactionHistory")
reader = cursor.arrow_reader(batch_size=50000)
# Process each batch with DuckDB
total_rows = 0
for batch in reader:
result = duckdb.sql("""
SELECT ProductID, SUM(ActualCost) AS TotalCost
FROM batch
GROUP BY ProductID
""")
total_rows += batch.num_rows
print(f"Processed {total_rows} rows")
스트리밍 결과를 누적하세요
모든 배치를 합산하려면 각 배치를 영구 DuckDB 연결에 등록하고 결과를 점진적으로 누적하세요.
cursor.execute("SELECT * FROM Production.TransactionHistory")
reader = cursor.arrow_reader(batch_size=50000)
duck = duckdb.connect()
duck.execute("CREATE TABLE transactions (ProductID INT, ActualCost DOUBLE, Quantity INT)")
for batch in reader:
duck.execute("INSERT INTO transactions SELECT ProductID, ActualCost, Quantity FROM batch")
# Query the accumulated data
result = duck.sql("""
SELECT ProductID, SUM(ActualCost) AS TotalCost, SUM(Quantity) AS TotalQty
FROM transactions
GROUP BY ProductID
ORDER BY TotalCost DESC
LIMIT 10
""")
print(result.fetchdf())
duck.close()
성능 팁
Microsoft SQL이 무거운 짐을 처리하게 하세요
Microsoft SQL은 모든 원시 데이터를 전선으로 가져오는 것보다 필터링, 조인, 집계가 더 빠릅니다. 이미 가져온 결과 집합에 대한 2차 분석에는 DuckDB를 사용하세요. SQL Server 쿼리 최적화를 대체하는 용도로는 사용하지 마세요.
# Suboptimal: Pull all rows, filter in DuckDB
cursor.execute("SELECT * FROM Sales.SalesOrderHeader")
orders = cursor.arrow()
result = duckdb.sql("SELECT * FROM orders WHERE TotalDue > 1000")
# Better: Filter in Microsoft SQL, analyze in DuckDB
cursor.execute("SELECT * FROM Sales.SalesOrderHeader WHERE TotalDue > 1000")
orders = cursor.arrow()
result = duckdb.sql("SELECT CustomerID, SUM(TotalDue) FROM orders GROUP BY CustomerID")
모든 읽기 작업에는 Arrow를 사용하세요
Arrow 기반 전송은 중간 Python 객체 생성을 피하여 메모리 사용량을 줄이고 처리량을 향상시킵니다. DuckDB에 데이터를 전달할 때는 데이터를 수동으로 한 행씩 변환하는 대신 cursor.arrow()를 사용하는 것이 좋습니다.
대규모 데이터셋에 스트리밍을 활용하세요
사용 가능한 메모리보다 큰 결과 세트의 경우 데이터를 점진적으로 처리하려면 batch_size 매개변수와 함께 arrow_reader()를 사용하세요.