mssql-python'u DuckDB ile kullanın

DuckDB, veri kopyalamadan Apache Arrow tablolarını doğrudan sorgulayabilen süreçte çalışan bir SQL analiz motorudur. DuckDB'yi mssql-python sürücüsü ile birleştirmek şunları sağlar:

  • Microsoft SQL sonuç kümelerinde verileri pandas veya Polars'a yüklemeden analitik SQL sorguları çalıştırın.
  • Bellekteki Arrow tablolarını sıfır kopyalama ek yükü olmadan sorgulayın.
  • Microsoft SQL verilerini yerel dosyalarla (CSV, Parquet, JSON) tek bir DuckDB sorgusunda birleştirin.
  • Microsoft SQL verilerini DuckDB üzerinden Parquet, CSV veya diğer formatlara aktarın.

Prerequisites

  • Python 3.10 veya üzeri.
  • mssql-python, duckdb ve pyarrow paketleri. Tümünü pip install mssql-python duckdb pyarrow ile yükleyin.
  • Tek seferlik işletim sistemine özgü önkoşulları yükleyin. Windows kullanıcıları bu adımı atlayabilir. Platformun tam detayları için mssql-python'u Install sayfasına bakınız.
    apk add libtool krb5-libs krb5-dev
    

SQL veritabanı oluşturma

Aşağıdaki platformlardan birinde bir SQL veritabanı oluşturun veya bağlanın:

Bu makaledeki örnekler örnek veritabanını AdventureWorks sorgulamaktadır. Henüz sahip değilseniz, AdventureWorks örnek veritabanlarına bakabilirsiniz.

Bağımlılıkları yükleme

pip install mssql-python duckdb pyarrow

DuckDB ile Microsoft SQL verilerini sorgulama

Temel iş akışı şudur: mssql-python ile bir sorgu çalıştırın, sonuçları bir Ok tablosu olarak alın, ardından o Ok tablosunu DuckDB SQL ile sorgulayın.

Temel desen

Bir bağlantı kurup verileri bir Ok tablosu olarak getirerek başlayın.

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 tablosuna products Python değişken adıyla atıfta bulunur. DuckDB'nin depolama alanına hiçbir veri kopyalanmaz.

Agregat ve filtre

DuckDB'nin SQL'ini kullanarak Arrow verilerini gruplayın ve birleştirin.

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

Birden fazla Microsoft SQL sonucuna katıl

Microsoft SQL'den birden fazla tablo getirin ve bunları DuckDB'ye katlayın, sunucular arası sorgu yazmadan.

# 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 verilerini yerel dosyalarla birleştir

DuckDB, CSV, Parquet ve JSON dosyalarını doğal olarak okuyabilir. SQL Server verilerini yerel dosyalarla tek bir sorguda birleştirin.

CSV dosyası ile birleştirin

Bir CSV dosyası yükleyin ve Microsoft SQL'den gelen verilerle birleştirin.

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 dosyası ile birleştirme

Bir Parquet dosyası yükleyin ve Microsoft SQL'den gelen verilerle birleştirin.

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 verilerini eksport et

DuckDB'COPYnin ifadesini kullanarak Microsoft SQL verilerini çeşitli dosya formatlarına aktarın.

Parquet olarak dışa aktar

Verileri Apache Parquet formatına aktarın.

cursor.execute("SELECT * FROM Production.Product")
products = cursor.arrow()

duckdb.sql("COPY products TO 'products.parquet' (FORMAT PARQUET)")

CSV’ye aktar

Veriyi virgülle ayrılmış bir değerler dosyasına aktarın:

cursor.execute("SELECT * FROM Sales.SalesOrderHeader")
orders = cursor.arrow()

duckdb.sql("COPY orders TO 'orders.csv' (FORMAT CSV, HEADER)")

Bölümlenmiş Parquet'i dışa aktar

Dağıtık analitik için verileri bölümlenmiş Parquet dosyalarına aktarın:

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

Büyük sonuç kümelerini akışla

Büyük veri setleri için, tüm satırları aynı anda belleğe yüklemeden akış gruplarında veri işlemek için kullanılır 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")

Yayın sonuçları biriktirin

Tüm partiler arasında toplamak için, her partiyi kalıcı bir DuckDB bağlantısına kaydedin ve sonuçları kademeli olarak biriktirin.

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

Performans ipuçları

Bırakın Microsoft SQL ağır işleri üstlensin

Microsoft SQL, filtreleme, birleştirme ve toplama işlemlerinde, tüm ham verileri kablo üzerinden çekmekten daha hızlıdır. DuckDB'yi, zaten alınmış sonuç kümelerinde ikincil analiz için kullanın, SQL Server sorgu optimizasyonunun yerine değil.

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

Tüm okuma işlemleri için Arrow kullanın

Ok tabanlı transfer, ara Python nesneleri oluşturmayı engeller; bu da bellek kullanımını azaltır ve veri verimliliğini artırır. DuckDB'ye veri aktarırken manuel satır satır dönüştürme yerine cursor.arrow() tercih edin.

Büyük veri kümeleri için akış kullanın

Kullanılabilir belleği aşan sonuç kümeleri için, verileri artımlı olarak işlemek üzere batch_size parametresiyle arrow_reader() kullanın.