Usa mssql-python con Apache Arrow

El controlador mssql-python proporciona métodos de obtención de Apache Arrow para la recuperación de datos columnares de alto rendimiento desde Microsoft SQL y Azure SQL Database.

Apache Arrow es una plataforma de desarrollo multilenguajes para datos columnares en memoria. El controlador convierte conjuntos de resultados ODBC directamente al formato Arrow en C++, evitando la creación de objetos en Python para mejorar el rendimiento.

La integración de flechas permite:

  • Transferencia de datos sin copia a Polars, pandas y DuckDB. "Copia cero" significa que los datos permanecen en un único búfer de memoria en el que el controlador escribe y del que las bibliotecas consumidoras leen directamente, por lo que no se duplican las filas en objetos intermedios de Python.
  • Transmitir conjuntos de resultados a través de RecordBatchReader sin cargarlo todo en memoria.
  • Formato de datos columnar ideal para cargas de trabajo analíticas y de aprendizaje automático.
  • Menor uso de memoria en comparación con la creación de objetos en Python fila por fila.

Métodos de cursor

El paquete pyarrow es necesario para usar los métodos de recuperación de Arrow. Instálelo con pip install pyarrow. Si pyarrow no está instalado, llamar a cualquier método Arrow genera un ImportError.

El controlador mssql-python añade tres métodos al objeto cursor para acceder a los datos de Arrow. Los tres métodos convierten los conjuntos de resultados ODBC al formato Arrow en la capa C++ del controlador, lo que evita crear objetos Python intermedios.

  • arrow() devuelve todo el conjunto de resultados como una sola tabla en memoria. Las más sencillas de utilizar.
  • arrow_batch() devuelve un lote de filas a la vez, dándote control manual sobre el bucle.
  • arrow_reader() devuelve un iterador que produce automáticamente lotes. Más adecuado para transmitir grandes volúmenes de resultados.

Uso de cursor.arrow(batch_size=8192)

Obtén todo el conjunto de resultados como un solo pyarrow.Table. Este método es el más sencillo y funciona bien cuando el conjunto completo de resultados cabe en la memoria.

import mssql_python

conn = mssql_python.connect(connection_string)
cursor = conn.cursor()

cursor.execute("SELECT ProductID, Name, ListPrice FROM Production.Product")
table = cursor.arrow()

print(type(table))       # <class 'pyarrow.lib.Table'>
print(table.num_rows)    # Number of rows fetched
print(table.num_columns) # Number of columns
print(table.schema)      # Column names and Arrow types
print(table.to_pandas()) # Convert to pandas DataFrame

Note

Si la cadena de conexión usa Authentication=ActiveDirectoryDefault, el controlador usa DefaultAzureCredential, que intenta usar varios proveedores de credenciales en secuencia. La primera conexión puede ser lenta porque el SDK recorre la cadena hasta encontrar un proveedor que funcione. En producción, si sabes qué tipo de credencial utiliza tu entorno, especifícala directamente (por ejemplo, ActiveDirectoryMSI para identidad gestionada) para evitar el recorrido en cadena. Para más información, consulte Autenticación de Microsoft Entra.

Uso de cursor.arrow_batch(batch_size=8192)

Busca una sola pyarrow.RecordBatch que contenga hasta batch_size filas. Utiliza este método para bucles personalizados de procesamiento por lotes donde necesitas un control detallado sobre cuántas filas se obtienen a la vez.

cursor.execute("SELECT * FROM Production.TransactionHistory")

while True:
    batch = cursor.arrow_batch(batch_size=10000)
    if batch.num_rows == 0:
        break
    # Process each batch
    print(f"Fetched {batch.num_rows} rows")

Uso de cursor.arrow_reader(batch_size=8192)

Devuelve un pyarrow.RecordBatchReader que produce objetos RecordBatch hasta agotar el conjunto de resultados. Este método es la opción más eficiente en memoria para conjuntos de resultados grandes.

cursor.execute("SELECT * FROM Production.TransactionHistory")
reader = cursor.arrow_reader(batch_size=50000)

for batch in reader:
    # Process streaming batches without loading all data
    print(f"Batch: {batch.num_rows} rows")

Patrones comunes

Las tablas de flechas se integran directamente con las populares bibliotecas de datos de Python. Los siguientes ejemplos muestran cómo pasar datos de Arrow a pandas, Polars, DuckDB y formatos de archivo sin copiar datos.

Cargar los resultados en pandas

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

# Convert to pandas with zero-copy where possible
df = table.to_pandas()
print(df.head())

Cargar resultados en Polars

import polars as pl

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

df = pl.from_arrow(table)
print(df)

Resultados de consulta con DuckDB

DuckDB puede consultar tablas de flechas directamente en SQL sin copiar datos. Esta capacidad es útil cuando necesitas un análisis al estilo SQL en conjuntos de resultados que ya están en formato Arrow.

import duckdb

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

# Query the Arrow table with DuckDB SQL
result = duckdb.sql("SELECT CustomerID, SUM(TotalDue) FROM arrow_table GROUP BY CustomerID")
print(result.fetchall())

Transmitir grandes conjuntos de resultados a Parquet

Para conjuntos de resultados grandes, transmite lotes de Arrow directamente a un archivo Parquet sin cargar todo el conjunto de datos en memoria. El ParquetWriter escribe cada lote de forma incremental.

import pyarrow.parquet as pq

cursor.execute("SELECT * FROM Production.TransactionHistory")
reader = cursor.arrow_reader(batch_size=100000)

# Write streaming batches to a Parquet file
writer = None
for batch in reader:
    if writer is None:
        writer = pq.ParquetWriter("output.parquet", batch.schema)
    writer.write_batch(batch)

if writer:
    writer.close()

Exportación a otros formatos

PyArrow proporciona escritores integrados para CSV y el formato de archivo IPC de Arrow (también conocido como Feather V2). Los archivos IPC de Arrow conservan exactamente los tipos de Arrow y son rápidos de volver a leer.

import pyarrow as pa
import pyarrow.csv as pcsv

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

# Write to CSV
pcsv.write_csv(table, "products.csv")

# Write to an Arrow IPC file
with pa.ipc.new_file("products.arrow", table.schema) as writer:
    writer.write_table(table)

Mapeos de tipos de datos

Los métodos de recuperación de Arrow asignan tipos SQL de Microsoft a tipos de Arrow a nivel de C++.

Tipo SQL de Microsoft Tipo de flecha
int, smallint, tinyint, bigint int32, int16, , int8, int64
flota, real float64, float32
decimal, numérico decimal128
bit bool
Char, Varchar, Nchar, Nvarchar utf8
texto, ntext large_utf8
binario, varbinario binary, large_binary
date date32
time time64[us]
datetime, datetime2, smalldatetime timestamp[us]
datetimeoffset timestamp[us, tz=UTC]
uniqueidentifier utf8 (cadena en mayúsculas)
xml utf8

Note

El controlador convierte el datetimeoffset tipo a UTC porque las columnas de flecha requieren una zona horaria fija. El controlador normaliza la información de huso horario por celda de Microsoft SQL a UTC durante la conversión.

El tipo sql_variant no es compatible con los métodos de recuperación de Arrow y genera una excepción de tipo de datos no compatible. Usa el estándar fetchone(), fetchmany(), o fetchall() para consultas que devuelven sql_variant columnas.

Consideraciones sobre el rendimiento

Los métodos de búsqueda de flechas son los más rápidos para análisis y operaciones masivas de datos, mientras que los métodos estándar de cursor son más adecuados para patrones transaccionales con conjuntos de resultados pequeños.

Cuándo usar Arrow frente al fetch estándar

Scenario Enfoque recomendado
Obtén unas cuantas filas para mostrarlas fetchone() / fetchall()
Cargar datos en pandas o polares cursor.arrow()
Procesar grandes conjuntos de datos en bloques cursor.arrow_reader()
Consultas de una sola fila o conjuntos de resultados pequeños fetchone() / fetchval()
Analítica o canalizaciones de agregación cursor.arrow() + Polars/DuckDB
Escribe resultados en Parquet o Arrow IPC cursor.arrow_reader() + PyArrow I/O

Gestión de memoria para grandes conjuntos de datos

Para conjuntos de resultados que puedan exceder la memoria disponible, úsalo arrow_reader() con un razonable batch_size.

cursor.execute("SELECT * FROM Production.TransactionHistory")

# Process in batches of 100K rows
reader = cursor.arrow_reader(batch_size=100000)
total_rows = 0

for batch in reader:
    # Work with each batch individually
    total_rows += batch.num_rows
    # batch goes out of scope and memory is freed

print(f"Processed {total_rows} rows")

Ajustar el tamaño del lote

El batch_size parámetro controla cuántas filas se obtienen en cada lote. El tamaño óptimo depende del ancho de fila y la memoria disponible. Las filas más anchas con columnas grandes como nvarchar(max) o varbinary(max) se benefician de lotes más pequeños, mientras que las filas estrechas se benefician de las más grandes.

  • Default (8192): Buen equilibrio para la mayoría de las cargas de trabajo.
  • Más pequeños (1000-5000): Úsalos para mesas anchas con columnas grandes.
  • Más grande (50000-100000): Úsalo para tablas estrechas o cuando el rendimiento importa más que la memoria.
# Narrow table with many rows - use larger batches
cursor.execute("SELECT ProductID, ListPrice FROM Production.Product")
table = cursor.arrow(batch_size=100000)

# Wide table with LOB columns - use smaller batches
cursor.execute("SELECT * FROM Production.Document")
table = cursor.arrow(batch_size=1000)