Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Le mssql-python pilote propose plusieurs chemins pour lire les données depuis Microsoft SQL. Chaque parcours correspond à des charges de travail différentes. Ce guide vous aide à choisir le bon en fonction de la taille de vos données, de vos besoins d’analyse et de vos exigences de performance.
Décidez par charge de travail
Utilisez ce tableau pour trouver votre point de départ :
| Charge de travail | Chemin recommandé | Pourquoi |
|---|---|---|
| Accès aux lignes d’applications (API web, CRUD) | Méthodes de récupération du curseur | Faible surcoût, traitement ligne par ligne, pas de dépendances supplémentaires. |
| Requêtes de reporting de petite à moyenne taille | pandas | API familière pour le filtrage, le regroupement et la visualisation. |
| Grands ensembles de résultats ou tables larges | Extraction des flèches | Transfert colonnaire sans copie, faible surcoût mémoire. |
| Analyse des performances élevée | Polaires avec flèche | Exécution multithreadée sur des données colonnaires, sans contention liée au GIL. |
| SQL ad hoc sur des données locales et distantes | DuckDB avec Arrow | Analyse SQL sur des tables Arrow, effectuer des jointures avec des fichiers CSV/Parquet locaux. |
| Exploration des notebooks | pandas ou polaires avec flèche | Choisissez en fonction de la familiarité de l’équipe et de la taille des données. |
Méthodes de récupération du curseur
Utilisez des méthodes de curseur standard lorsque vous avez besoin d’un accès orienté ligne sans dépendances supplémentaires. Cette méthode est le bon choix pour le code applicatif qui traite une ligne à la fois, renvoie des réponses API ou alimente la logique applicative.
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()
Utilisation fetchmany() pour le traitement par lots économe en mémoire de grands ensembles de résultats. Utilisez fetchval() lorsque vous avez besoin d’une seule valeur, comme un nombre, un maximum ou une vérification d’existence.
Pour la documentation complète de la méthode de récupération, voir Récupérer les données.
Extraction des flèches
Utilisez l’extraction par flèches lorsque vous avez besoin de données en colonnes pour l’analyse, la construction de DataFrames, ou l’exportation vers Parquet. Arrow fournit un transfert de données sans copie depuis le pilote, ce qui évite la surcharge de conversion ligne par ligne qu’implique la création d’un DataFrame à partir de fetchall().
Les tables avec index de colonne sont déjà stockées au format columnaire dans le moteur de base de données, ce qui fait de l’extraction Arrow un choix naturel pour ces charges de travail.
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")
Pour de grands ensembles de résultats, utilisez arrow_reader() pour streamer des lots sans tout charger en mémoire :
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")
Les tables Arrow servent de point de départ à pandas, Polars et DuckDB. Extraire une fois, puis convertir :
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)
Pour la documentation complète d’Arrow, voir intégration d’Apache Arrow.
pandas
Utilisez pandas lorsque vous avez besoin d’une API DataFrame familière pour le rapport, l’analyse ad hoc ou le nettoyage de données. Pandas fonctionne mieux avec des ensembles de résultats qui tiennent en mémoire (jusqu’à quelques millions de lignes, selon la largeur de la colonne).
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"]))
Pour des ensembles de résultats plus grands, construisez le DataFrame à partir d’Arrow au lieu de fetchall():
cursor.execute("SELECT * FROM Sales.SalesOrderHeader")
arrow_table = cursor.arrow()
df = arrow_table.to_pandas()
Pour découvrir les cas d’usage pandas complets, y compris l’ETL, les séries temporelles et la réécriture, voir l’intégration pandas.
Polars avec Arrow
Utilisez les polars lorsque vous avez besoin d’opérations DataFrame plus rapides sur de plus grands ensembles de résultats. Polars utilise Apache Arrow comme format mémoire, donc le transfert depuis cursor.arrow() est en copie zéro. Polars exécute également des opérations sur plusieurs threads, ce qui évite la contention GIL lors des transformations à forte intensité 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)
Pour diffuser de larges ensembles de résultats :
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)
Pour des modèles Polars complets, voir l’intégration de Polars.
DuckDB avec Arrow
Utilisez DuckDB lorsque vous devez exécuter des analyses SQL sur des données extraites, joindre les données serveur avec des fichiers CSV ou Parquet locaux, ou exporter des résultats vers des formats de fichiers. DuckDB fonctionne sur des tables Arrow avec un accès sans copie.
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())
Rejoindre les données du serveur avec un fichier local :
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
""")
Exporter au format Parquet :
cursor.execute("SELECT * FROM Sales.SalesOrderHeader")
orders = cursor.arrow()
duckdb.sql("COPY orders TO 'orders.parquet' (FORMAT PARQUET)")
Pour les patrons complets de DuckDB, voir intégration DuckDB.
Fonctionnalités de Microsoft SQL qui influencent les décisions de chemin de lecture
Le moteur de base de données dispose de fonctionnalités qui influencent directement quel chemin de lecture fonctionne le mieux. Prenez en compte ces caractéristiques lors du choix de votre approche :
Indexes columnstore
Les tables avec des index columnstore stockent les données dans un format colonnaire. L’extraction par flèches est la transmission naturelle pour ces tables car les données sont déjà en colonnes dans le moteur. Si vos requêtes analytiques scannent des tables larges de millions de lignes, un index de colonstock non clusterisé côté serveur, combiné à une extraction Arrow côté client, offre le meilleur débit de bout en bout.
Vues indexées
Les vues indexées précalculent et stockent les résultats agrégés ou joints sur le serveur. Si votre analyse avec pandas ou Polars recalcule régulièrement la même agrégation, envisagez de créer une vue indexée et de l’interroger à la place. Le serveur maintient automatiquement la vue au fur et à mesure que les données sous-jacentes changent.
Magasin des requêtes (magasin de requêtes)
Magasin des requêtes suit les statistiques d’exécution des requêtes au fil du temps. Utilisez-le pour identifier quelles requêtes sont suffisamment coûteuses pour justifier une extraction Arrow et une analyse locale DataFrame par rapport à une lecture directe du curseur. Si une requête s’exécute en quelques millisecondes, l’extraction via curseur convient. S’il scanne des millions de lignes, l’extraction de flèches et l’analyse locale pourraient réduire la charge du serveur.
Traitement intelligent des requêtes
Les fonctionnalités intelligentes de traitement des requêtes de Microsoft SQL, telles que les jointures adaptatives, le mode batch sur rowstore et le retour d'information de la reconnaissance mémoire, optimisent automatiquement l'exécution des requêtes. Ces fonctionnalités fonctionnent quel que soit le chemin de lecture client choisi, mais elles bénéficient surtout aux grandes requêtes analytiques. Vous n’avez pas besoin d’ajuster les indices ou les plans d’exécution pour la plupart des charges de travail.
Traiter en flux de grands ensembles de résultats
Pour les ensembles de résultats qui ne rentrent pas en mémoire, utilisez des motifs de flux :
Streaming basé sur un curseur avec 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
Streaming à flèches vers 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()
Antipatrons à éviter
| Anti-modèles | Problème | Meilleure approche |
|---|---|---|
fetchall() puis pd.DataFrame() pour les grands tableaux |
Charge toutes les lignes en mémoire deux fois (une fois en tant que tuples, une fois en DataFrame). | Utilisez cursor.arrow() alors arrow_table.to_pandas(). |
| Convertir Arrow en pandas juste pour filtrer les lignes | Ça gaspille de la mémoire sur la copie complète de Pandas. | Filtrez dans SQL (WHERE clause ) ou utilisez directement Polars/DuckDB sur la table Arrow. |
SELECT * Quand il faut trois colonnes |
Transfert de données inutiles depuis le serveur. | Listez uniquement les colonnes dont vous avez besoin. |
Construire un DataFrame pour calculer COUNT(*) |
Le serveur calcule les agrégats plus rapidement que Python. | Utiliser SELECT COUNT(*) et fetchval(). |
| Ouverture d’une nouvelle connexion par requête | La création d’une connexion est coûteuse, même avec la surcharge liée au pooling. | Réutilisez les connexions dans une unité de travail logique. |
| Flèche de Chaîne -> Pandas -> Polars | Chaque conversion copie les données. | Accédez directement à votre format cible : Arrow -> Polars ou Arrow -> pandas. |