Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
O mssql-python driver fornece múltiplos caminhos para ler dados do Microsoft SQL. Cada opção adequa-se a diferentes cargas de trabalho. Este guia ajuda-o a escolher o mais adequado com base no tamanho dos seus dados, necessidades de análise e requisitos de desempenho.
Decide por carga de trabalho
Use esta tabela para encontrar o seu ponto de partida:
| Carga de trabalho | Caminho recomendado | Porquê |
|---|---|---|
| Acesso à linha de aplicações (web API, CRUD) | Métodos de busca por cursor | Baixa sobrecarga, processamento linha a linha, sem dependências extra. |
| Consultas de relatórios de pequena a média dimensão | pandas | API familiar para filtragem, agrupamento e visualização. |
| Grandes conjuntos de resultados ou tabelas largas | Extração de flechas | Transferência colunar sem cópia, sobrecarga de memória mínima. |
| Análise de elevado desempenho | Polares com Flecha | Execução com múltiplas threads em dados colunares, sem contenção do GIL. |
| SQL ad hoc sobre dados locais e remotos | DuckDB com Flecha | Análises SQL sobre tabelas Arrow, combinar com ficheiros CSV/Parquet locais. |
| Exploração de cadernos | pandas ou Polars com Flecha | Escolha com base na familiaridade da equipa e no tamanho dos dados. |
Métodos de busca por cursor
Use métodos padrão de cursor quando precisar de acesso orientado a linhas sem dependências adicionais. Este método é a escolha certa para código de aplicação que processa uma linha de cada vez, devolve respostas da API ou alimenta a lógica da aplicação.
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()
Utilize fetchmany() para o processamento em lote de grandes conjuntos de resultados com utilização eficiente da memória. Usa fetchval() quando precisares de um único valor, como uma contagem, um máximo ou uma verificação de existência.
Para documentação completa do método fetch, veja Recuperar dados.
Extração de flechas
Utilize a extração Arrow quando precisar de dados colunares para análise de dados, a construção de DataFrames ou exportação para Parquet. O Arrow fornece transferência de dados sem cópia a partir do controlador, o que evita a sobrecarga de conversão linha a linha ao criar um DataFrame a partir de fetchall().
As tabelas com índices de columnstore já estão armazenadas em formato colunar no motor da base de dados, tornando a extração por Arrow uma escolha natural para essas cargas de trabalho.
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")
Para conjuntos de resultados grandes, use arrow_reader() para transmitir lotes sem carregar tudo na memória:
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")
As tabelas Arrow são o ponto de partida para pandas, Polars e DuckDB. Extrai uma vez, depois converte:
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)
Para documentação completa do Arrow, veja integração com o Apache Arrow.
pandas
Use pandas quando precisar de uma API DataFrame familiar para relatórios, análises ad hoc ou limpeza de dados. O Pandas funciona melhor com conjuntos de resultados que cabem na memória (até alguns milhões de linhas, dependendo da largura da coluna).
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"]))
Para conjuntos de resultados maiores, constrói o DataFrame a partir do Arrow em vez de fetchall():
cursor.execute("SELECT * FROM Sales.SalesOrderHeader")
arrow_table = cursor.arrow()
df = arrow_table.to_pandas()
Para padrões completos do pandas, incluindo ETL, séries temporais e write-back, veja integração do pandas.
Polares com Flecha
Use Polars quando precisar de operações DataFrame mais rápidas em conjuntos de resultados maiores. O Polars utiliza o Apache Arrow como formato de memória, pelo que a transferência de cursor.arrow() é zero-copy. O Polars também executa operações em várias threads, o que evita a contenção do GIL em transformações intensivas em 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)
Para streaming de grandes conjuntos de resultados:
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)
Para padrões completos de Polars, veja integração de Polars.
DuckDB com Arrow
Use o DuckDB quando precisar de executar análises SQL em dados extraídos, juntar dados do servidor com ficheiros CSV ou Parquet locais, ou exportar resultados para formatos de ficheiro. O DuckDB opera em tabelas Arrow com acesso sem cópia.
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())
Juntar os dados do servidor com um ficheiro 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
""")
Exportar para Parquet:
cursor.execute("SELECT * FROM Sales.SalesOrderHeader")
orders = cursor.arrow()
duckdb.sql("COPY orders TO 'orders.parquet' (FORMAT PARQUET)")
Para padrões completos do DuckDB, veja integração com o DuckDB.
Funcionalidades do SQL Server da Microsoft que afetam as decisões do trajeto de leitura
O motor de base de dados tem funcionalidades que afetam diretamente qual o caminho de leitura que funciona melhor. Considere estas características ao escolher a sua abordagem:
Índices de Columnstore
Tabelas com índices de coluna armazenam dados em formato colunar. A extração em Arrow é a forma natural de transferência para estas tabelas, porque os dados já estão em formato colunar no motor. Se as suas consultas analíticas processam tabelas largas com milhões de linhas, um índice columnstore não clusterizado no lado do servidor, combinado com a extração em Arrow no lado do cliente, proporciona a melhor taxa de transferência de ponta a ponta.
Visualizações indexadas
As visualizações indexadas pré-computam e armazenam resultados agregados ou juntos no servidor. Se a sua análise com pandas ou Polars calcula repetidamente a mesma agregação, considere criar uma vista indexada e consultar essa vista em vez dela. O servidor mantém automaticamente a visualização à medida que os dados subjacentes mudam.
Query Store
A Query Store acompanha estatísticas de execução de consultas ao longo do tempo. Utilize-o para identificar quais as consultas suficientemente dispendiosas para justificar uma extração com Arrow e uma análise local em DataFrame, em vez de uma leitura direta do cursor. Se uma consulta é executada em milissegundos, a obtenção por cursor é adequada. Se forem analisadas milhões de linhas, a extração em Arrow e a análise local podem reduzir a carga no servidor.
Processamento inteligente de consultas
As funcionalidades inteligentes de processamento de consultas do Microsoft SQL, como joins adaptativos, modo batch no rowstore e feedback de concessão de memória, otimizam automaticamente a execução das consultas. Estas funcionalidades funcionam independentemente do caminho de leitura do cliente escolhido, mas são as que mais beneficiam as grandes consultas analíticas. Não precisas de ajustar dicas ou planos de execução para a maioria das cargas de trabalho.
Transmita grandes conjuntos de resultados
Para conjuntos de resultados que não cabem na memória, use padrões de streaming:
Transmissão baseada em cursor com 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 baseado em Arrow para 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()
Anti-padrões a evitar
| Anti-padrão | Problema | Abordagem melhor |
|---|---|---|
fetchall() depois pd.DataFrame() , para tabelas grandes |
Carrega todas as linhas na memória duas vezes (uma como tuplas, outra como DataFrame). | Utilize cursor.arrow() depois arrow_table.to_pandas(). |
| Converter o Arrow em pandas só para filtrar linhas | Desperdiça memória na cópia completa do pandas. | Filtre em SQL (cláusula WHERE) ou use Polars/DuckDB diretamente sobre a tabela Arrow. |
SELECT * Quando precisas de três colunas |
Transfere dados desnecessários do servidor. | Liste apenas as colunas de que precisa. |
Construir um DataFrame para calcular COUNT(*) |
O servidor calcula agregados mais rapidamente do que o Python. | Usar SELECT COUNT(*) e fetchval(). |
| Abrir uma nova ligação por consulta | A criação de ligações é dispendiosa mesmo com a sobrecarga do agrupamento de ligações. | Reutilize ligações dentro de uma unidade lógica de trabalho. |
| Flecha de Encadeamento -> pandas -> Polars | Cada conversão copia os dados. | Vá diretamente para o seu formato de destino: Arrow -> Polars ou Arrow -> pandas. |