Vyberte vzor přístupu k datům a analytiky pomocí mssql-python

Ovladač mssql-python poskytuje více cest pro čtení dat z Microsoft SQL. Každá cesta odpovídá jiným pracovním zátěžím. Tento průvodce vám pomůže vybrat ten správný na základě velikosti vašich dat, potřeb analýzy a požadavků na výkon.

Rozhodujte podle pracovní zátěže

Použijte tuto tabulku k nalezení výchozího bodu:

Pracovní zátěž Doporučená cesta Proč
Přístup k řádkům aplikace (webové API, CRUD) Metody načítání pomocí kurzoru Minimální režie, zpracování po jednotlivých řádcích, žádné další závislosti.
Malé až střední dotazy na reportování pandas Známé API pro filtrování, seskupování a vizualizaci.
Velké množiny výsledků nebo široké tabulky Extrakce šípů Sloupcový přenos bez kopírování, minimální paměťová zátěž.
Vysoce výkonné analýzy Polars s Arrow Vícevláknové provedení na sloupcových datech, bez GIL sporů.
Ad hoc SQL přes lokální a vzdálená data DuckDB s využitím Arrow SQL analytika nad tabulkami Arrow, spojování s lokálními soubory CSV/Parquet.
Prozkoumání notebooku pandy nebo polári se šípem Vyberte podle znalosti týmu a velikosti dat.

Metody načítání pomocí kurzoru

Používejte standardní metody kurzoru, když potřebujete přístup orientovaný na řádky bez dalších závislostí. Tato metoda je správnou volbou pro aplikační kód, který zpracovává jeden řádek po druhém, vrací odpovědi API nebo poskytuje aplikační logiku.

import mssql_python

conn = mssql_python.connect(
    server="<server>.database.windows.net",
    database="<database>",
    authentication="ActiveDirectoryDefault",
    encrypt="yes"
)

cursor = conn.cursor()

# fetchone(): Process rows one at a time
cursor.execute("SELECT ProductID, Name, ListPrice FROM Production.Product WHERE ListPrice > %(threshold)s", {"threshold": 100})
row = cursor.fetchone()
while row:
    print(f"{row.Name}: ${row.ListPrice:.2f}")
    row = cursor.fetchone()

# fetchmany(): Process in batches
cursor.execute("SELECT ProductID, Name FROM Production.Product")
while True:
    batch = cursor.fetchmany(100)
    if not batch:
        break
    for row in batch:
        print(row.Name)

# fetchval(): Get a single scalar value
cursor.execute("SELECT COUNT(*) FROM Production.Product")
count = cursor.fetchval()

Použití fetchmany() pro paměťově efektivní dávkové zpracování velkých výsledků. Použij, fetchval() když potřebuješ jednu hodnotu, například count, max nebo exists check.

Pro kompletní dokumentaci metody načtení viz Retrive data.

Extrakce šípů

Použijte extrakci šípek, když potřebujete sloupcová data pro analytiku, tvorbu DataFrame nebo export do Parquet. Arrow umožňuje přenos dat od ovladače bez kopírování, čímž se zabrání režii převodu po jednotlivých řádcích při vytváření objektu DataFrame z fetchall().

Tabulky s indexy columnstore jsou již v databázovém stroji uloženy ve sloupcovém formátu, takže extrakce do formátu Arrow je pro tento typ úloh přirozeně vhodným řešením.

cursor.execute("""
    SELECT ProductID, Name, ListPrice, Color
    FROM Production.Product
    WHERE ListPrice > 0
""")

# Get a single Arrow table
arrow_table = cursor.arrow()
print(f"{arrow_table.num_rows} rows, {arrow_table.num_columns} columns")

Pro velké sady výsledků použijte arrow_reader() ke streamování dávek bez načítání všeho do paměti:

cursor.execute("SELECT * FROM Sales.SalesOrderHeader")

# Stream Arrow record batches
reader = cursor.arrow_reader(batch_size=10000)
for batch in reader:
    # Each batch is a pyarrow.RecordBatch
    print(f"Batch: {batch.num_rows} rows")

Tabulky Arrow jsou výchozím bodem pro pandas, Polars a DuckDB. Jednou extrahujte a pak převeďte:

cursor.execute("""
    SELECT ProductID, Name, ListPrice, Color
    FROM Production.Product
    WHERE ListPrice > 0
""")
arrow_table = cursor.arrow()

# Arrow -> pandas
df = arrow_table.to_pandas()

# Arrow -> Polars (zero-copy)
import polars as pl
df = pl.from_arrow(arrow_table)

Pro kompletní dokumentaci Arrow viz integrace Apache Arrow.

pandas

Používejte pandas, když potřebujete známé DataFrame API pro reportování, ad hoc analýzu nebo čištění dat. Pandas nejlépe funguje s výsledkovými sadami, které se vejdou do paměti (až několik milionů řádků, v závislosti na šířce sloupce).

cursor.execute("""
    SELECT p.Name, p.ListPrice, pc.Name AS Category
    FROM Production.Product p
    JOIN Production.ProductSubcategory ps ON p.ProductSubcategoryID = ps.ProductSubcategoryID
    JOIN Production.ProductCategory pc ON ps.ProductCategoryID = pc.ProductCategoryID
    WHERE p.ListPrice > 0
""")

import pandas as pd
rows = cursor.fetchall()
columns = [desc[0] for desc in cursor.description]
df = pd.DataFrame.from_records(rows, columns=columns)

# Analyze
print(df.groupby("Category")["ListPrice"].agg(["mean", "count"]))

Pro větší sady výsledků vytvořte DataFrame z formátu Arrow místo fetchall():

cursor.execute("SELECT * FROM Sales.SalesOrderHeader")
arrow_table = cursor.arrow()
df = arrow_table.to_pandas()

Úplný přehled scénářů použití knihovny pandas včetně ETL, časových řad a zpětného zápisu najdete v části integrace pandas.

Polars with Arrow

Polars používej, když potřebuješ rychlejší DataFrame operace na větších výsledkových sadách. Polars používá Apache Arrow jako svůj paměťový formát, takže přenos z cursor.arrow() probíhá bez kopírování. Polars také provádí operace na více vláknech, což zabraňuje sporům o GIL při transformacích náročných na CPU.

import polars as pl

cursor.execute("""
    SELECT ProductID, Name, ListPrice, Color
    FROM Production.Product
    WHERE ListPrice > 0
""")

arrow_table = cursor.arrow()
df = pl.from_arrow(arrow_table)

# Filter and aggregate
result = (
    df.filter(pl.col("ListPrice") > 100)
    .group_by("Color")
    .agg(pl.col("ListPrice").mean().alias("AvgPrice"))
    .sort("AvgPrice", descending=True)
)
print(result)

Pro streamování velkých sad výsledků:

cursor.execute("SELECT * FROM Sales.SalesOrderHeader")

reader = cursor.arrow_reader(batch_size=50000)
frames = []
for batch in reader:
    frames.append(pl.from_arrow(batch))

df = pl.concat(frames)

Úplné ukázky pro Polars najdete v části Integrace Polars.

DuckDB s Arrow

Používejte DuckDB, když potřebujete spustit SQL analytiku na extrahovaných datech, spojit data serveru s lokálními CSV nebo Parquet soubory, nebo exportovat výsledky do formátů souborů. DuckDB pracuje na tabulkách Arrow s přístupem bez kopírování.

import duckdb

cursor.execute("""
    SELECT ProductID, Name, ListPrice, Color
    FROM Production.Product
    WHERE ListPrice > 0
""")

products = cursor.arrow()

# Run DuckDB SQL on the Arrow table
result = duckdb.sql("""
    SELECT Color, AVG(ListPrice) AS AvgPrice, COUNT(*) AS Count
    FROM products
    WHERE Color IS NOT NULL
    GROUP BY Color
    ORDER BY AvgPrice DESC
""")
print(result.fetchdf())

Připojení serverových dat s lokálním souborem:

cursor.execute("SELECT CustomerID, TerritoryID FROM Sales.Customer")
customers = cursor.arrow()

# Join with a local CSV file
result = duckdb.sql("""
    SELECT c.CustomerID, c.TerritoryID, l.Region
    FROM customers c
    JOIN read_csv_auto('regions.csv') l ON c.TerritoryID = l.TerritoryID
""")

Export do parkety:

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

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

Pro kompletní vzory DuckDB viz integrace DuckDB.

Funkce Microsoft SQL, které ovlivňují rozhodnutí týkající se cesty čtení

Databázový engine má funkce, které přímo ovlivňují, která čtecí cesta je nejlepší. Při výběru přístupu zvažte tyto vlastnosti:

Indexy sloupcové

Tabulky s indexy columnstore ukládají data ve sloupcovém formátu. Extrakce šipky je přirozeným předáním těchto tabulek, protože data jsou již sloupcová v enginu. Pokud vaše analytické dotazy procházejí široké tabulky s miliony řádků, neskupený sloupcový index na straně serveru v kombinaci s extrakcí Arrow na straně klienta poskytuje nejlepší celkovou propustnost.

Indexovaná zobrazení

Indexované zobrazení předpočítají a ukládají agregované nebo připojené výsledky na server. Pokud vaše analýza Pandas nebo Polars opakovaně počítá stejnou agregaci, zvažte vytvoření indexovaného pohledu a dotazování tohoto pohledu místo toho. Server automaticky udržuje zobrazení při změnách podkladových dat.

úložiště dotazů

Query Store sleduje statistiky provádění dotazů v čase. Použijte ho k identifikaci, které dotazy jsou dostatečně drahé na to, aby ospravedlnily extrakci Arrow a lokální analýzu DataFrame oproti přímému čtení kurzoru. Pokud dotaz běží v řádu milisekund, načítání pomocí kurzoru je v pořádku. Pokud prohledá miliony řádků, extrakce šípek a lokální analýza mohou snížit zatížení serveru.

Inteligentní zpracování dotazů

Inteligentní funkce zpracování dotazů Microsoft SQL, jako jsou adaptivní spojení, dávkový režim na rowstore a zpětná vazba z přidělování paměti, automaticky optimalizují provádění dotazů. Tyto funkce fungují bez ohledu na to, kterou cestu čtení klienta zvolíte, ale nejvíce prospívají velkým analytickým dotazům. U většiny pracovních zátěží nemusíte ladit hinty ani plány vykonávání.

Streamovat rozsáhlé sady výsledků

Pro sady výsledků, které se nevejdou do paměti, použijte streamovací vzory:

Streamování pomocí kurzoru s fetchmany():

cursor.execute("SELECT * FROM Sales.SalesOrderHeader")
while True:
    batch = cursor.fetchmany(5000)
    if not batch:
        break
    for row in batch:
        print(row[0])  # Process each row

Streamování založené na Apache Arrow do Parquet:

import pyarrow.parquet as pq

cursor.execute("SELECT * FROM Sales.SalesOrderHeader")
reader = cursor.arrow_reader(batch_size=50000)
writer = None

for batch in reader:
    if writer is None:
        writer = pq.ParquetWriter("orders.parquet", batch.schema)
    writer.write_batch(batch)

if writer:
    writer.close()

Antivzory, kterým se vyhnout

Vzor neefektivního řešení Problém Lepší přístup
fetchall() pak pd.DataFrame() pro velké tabulky Načte všechny řádky do paměti dvakrát (jednou jako ntice, jednou ve formě DataFrame). Použijte cursor.arrow() pak arrow_table.to_pandas().
Převádění z Arrow do pandas jen kvůli filtrování řádků Plýtvá to pamětí na plnou kopii Panda. Filtrujte v SQL (WHERE klauzule) nebo použijte Polars/DuckDB přímo v tabulce Arrow.
SELECT * když potřebujete tři sloupce Přenáší zbytečná data ze serveru. Uveďte jen ty sloupce, které potřebujete.
Vytvoření DataFrame pro výpočet COUNT(*) Server počítá agregace rychleji než Python. Použijte SELECT COUNT(*) a fetchval().
Otevření nového připojení pro každý dotaz Vytváření připojení je nákladné i při použití sdružování připojení. Znovu použijte spojení v rámci logické jednotky práce.
Řetězový šíp -> pandy -> polární Každá konverze kopíruje data. Přejděte přímo do cílového formátu: Arrow -> Polars nebo Arrow -> pandas.