Verwenden Sie mssql-python mit DuckDB

DuckDB ist eine in-process SQL-Analyse-Engine, die Apache Arrow-Tabellen direkt abfragen kann, ohne Daten zu kopieren. Die Kombination von DuckDB mit dem mssql-python-Treiber ermöglicht es:

  • Führe analytische SQL-Abfragen auf Microsoft SQL-Ergebnissätzen aus, ohne Daten in Pandas oder Polars zu laden.
  • Abfragen von Arrow-Tabellen im Speicher ohne Kopier-Overhead.
  • Verknüpfen Sie Microsoft SQL-Daten mit lokalen Dateien (CSV, Parquet, JSON) in einer einzigen DuckDB-Abfrage.
  • Exportiere Microsoft SQL-Daten über DuckDB in Parquet, CSV oder andere Formate.

Voraussetzungen

  • Python 3.10 oder höher.
  • Die mssql-python, duckdb, und pyarrow Pakete. Installieren Sie alle mit pip install mssql-python duckdb pyarrow.
  • Installieren Sie die einmaligen betriebsystem-spezifischen Voraussetzungen. Windows-Nutzer können diesen Schritt überspringen. Für vollständige Plattformdetails siehe Install mssql-python.
    apk add libtool krb5-libs krb5-dev
    

Erstellen einer SQL-Datenbank

Erstellen oder verbinden Sie sich mit einer SQL-Datenbank auf einer der folgenden Plattformen:

Die Beispiele in diesem Artikel fragen die AdventureWorks Beispieldatenbank ab. Falls du sie noch nicht hast, schau dir die AdventureWorks-Beispieldatenbanken an.

Abhängigkeiten installieren

pip install mssql-python duckdb pyarrow

Abfrage von Microsoft-SQL-Daten mit DuckDB

Der grundlegende Arbeitsablauf ist: Eine Abfrage mit mssql-python ausführen, die Ergebnisse als Arrow-Tabelle abrufen und dann diese Arrow-Tabelle mit DuckDB SQL abfragen.

Grundlegendes Muster

Beginnen Sie damit, eine Verbindung herzustellen und Daten als Pfeiltabelle abzurufen.

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 verweist auf die products Arrow-Tabelle anhand ihres Python-Variablennamens. Es werden keine Daten in den Speicher von DuckDB kopiert.

Aggregat und Filter

Verwenden Sie das SQL von DuckDB, um Arrow-Daten zu gruppieren und zu aggregieren.

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

Treten Sie mehreren Microsoft SQL-Ergebnissen bei

Mehrere Tabellen aus Microsoft SQL holen und sie in DuckDB einfügen, ohne eine serverübergreifende Abfrage zu schreiben.

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

Verknüpfe Microsoft SQL-Daten mit lokalen Dateien

DuckDB kann CSV-, Parquet- und JSON-Dateien nativ lesen. Kombinieren Sie SQL Server-Daten mit lokalen Dateien in einer einzigen Abfrage.

Mit einer CSV-Datei verknüpfen

Lade eine CSV-Datei und verbinde sie mit Daten von 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)

Mit einer Parquet-Datei verbinden

Lade eine Parquet-Datei und verbinde sie mit Daten aus 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-Daten exportieren

Verwenden Sie die COPY DuckDB-Anweisung, um Microsoft SQL-Daten in verschiedene Dateiformate zu exportieren.

Export nach Parquet

Daten in das Apache-Parquet-Format exportieren.

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

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

In CSV exportieren

Exportiere die Daten in eine Komma-getrennte Wertedatei:

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

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

Partitioniertes Parquet exportieren

Exportiere Daten in partitionierte Parquet-Dateien für verteilte Analysen:

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

Große Ergebnismengen streamen

Verwenden Sie für große Datensätze arrow_reader(), um Daten in Streaming-Batches zu verarbeiten, ohne alle Zeilen gleichzeitig in den Arbeitsspeicher zu laden:

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

Streaming-Ergebnisse akkumulieren

Um über alle Chargen hinweg zu aggregieren, registrieren Sie jeden Batch in einer persistenten DuckDB-Verbindung und akkumulieren Sie die Ergebnisse schrittweise.

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

Leistungstipps

Lass Microsoft SQL die schwere Arbeit übernehmen

Microsoft SQL ist schneller für Filterung, Joins und Aggregationen, als alle Rohdaten über die Leitung zu ziehen. Verwenden Sie DuckDB für sekundäre Analysen von bereits abgerufenen Ergebnismengen, nicht als Ersatz für die SQL Server-Abfrageoptimierung.

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

Verwenden Sie Arrow für alle Leseoperationen

Pfeilbasierte Übertragungen vermeiden das Erstellen von Zwischen-Python-Objekten, was den Speicherbedarf reduziert und den Durchsatz verbessert. Bevorzuge cursor.arrow() statt einer manuellen zeilenweisen Konvertierung beim Übergeben von Daten an DuckDB.

Nutzen Sie Streaming für große Datensätze

Für Ergebnismengen, die größer sind als der verfügbaren Speicher, verwenden Sie arrow_reader() mit einem batch_size-Parameter, um Daten inkrementell zu verarbeiten.