Użyj mssql-python z Apache Arrow

Sterownik mssql-python udostępnia metody pobierania danych w formacie Apache Arrow, umożliwiające wydajne pobieranie danych kolumnowych z Microsoft SQL i Azure SQL Database.

Apache Arrow to wielojęzyczna platforma programistyczna do przetwarzania kolumnowych danych w pamięci. Sterownik konwertuje zestawy wyników ODBC bezpośrednio na format Arrow w C++, omijając tworzenie obiektów w Python dla poprawy wydajności.

Integracja Arrow umożliwia:

  • Transfer danych bez kopiowania do Polars, Pandas i DuckDB. „Zero-copy” oznacza, że dane pozostają w jednym buforze pamięci, do którego sterownik zapisuje dane, a biblioteki korzystające odczytują je bezpośrednio, więc żaden wiersz nie jest kopiowany do pośrednich obiektów Pythona.
  • Strumieniowe przesyłanie zestawów wyników za pośrednictwem RecordBatchReader bez wczytywania wszystkiego do pamięci.
  • Kolumnowy format danych idealny do zadań związanych z analityką i uczeniem maszynowym.
  • Zmniejszone zużycie pamięci w porównaniu do tworzenia obiektów w Python wiersz po wierszu.

Metody kursora

Pakiet pyarrow jest wymagany do korzystania z metod pobierania Arrow. Zainstaluj go za pomocą polecenia pip install pyarrow. Jeśli pyarrow nie jest zainstalowany, wywołanie dowolnej metody Arrow powoduje ImportError.

Sterownik mssql-python dodaje trzy metody do obiektu kursora do dostępu do danych Arrow. Wszystkie trzy metody konwertują zestawy wyników ODBC na format Arrow w warstwie C++ sterownika, co pozwala uniknąć tworzenia pośrednich obiektów Python.

  • arrow() zwraca cały zbiór wyników jako jedną tablicę w pamięci. Najprostsze w użyciu.
  • arrow_batch() Zwraca jedną partię wierszy naraz, zapewniając ręczną kontrolę nad pętlą.
  • arrow_reader() zwraca iterator, który automatycznie generuje partie. Najlepsze do streamowania dużych wyników.

Korzystanie z cursor.arrow(batch_size=8192)

Pobierz cały zbiór wyników jako pojedynczy pyarrow.Table. Ta metoda jest najprostsza i dobrze działa, gdy cały zbiór wyników mieści się w pamięci.

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

Jeśli w parametrach połączenia użyto Authentication=ActiveDirectoryDefault, sterownik używa DefaultAzureCredential, który po kolei próbuje użyć wielu dostawców poświadczeń. Pierwsze połączenie może być wolne, ponieważ SDK przechodzi przez łańcuch, aż znajdzie dostawcę, który działa. W środowisku produkcyjnym, jeśli wiesz, jakiego typu poświadczeń używa środowisko, wskaż go bezpośrednio (na przykład ActiveDirectoryMSI w przypadku tożsamości zarządzanej), aby uniknąć przechodzenia przez łańcuch. Aby uzyskać więcej informacji, zobacz Microsoft Entra authentication (Uwierzytelnianie w usłudze Microsoft Entra).

Korzystanie z cursor.arrow_batch(batch_size=8192)

Pobierz pojedynczy pyarrow.RecordBatch, zawierający maksymalnie batch_size wierszy. Użyj tej metody w przypadku niestandardowych pętli przetwarzania wsadowego, gdy potrzebujesz szczegółowej kontroli nad liczbą wierszy pobieranych jednocześnie.

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

Korzystanie z cursor.arrow_reader(batch_size=8192)

Zwróć pyarrow.RecordBatchReader, która zwraca obiekty RecordBatch do wyczerpania zestawu wyników. Ta metoda jest najbardziej efektywną pod względem pamięci opcją dla dużych zbiorów wyników.

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

Często używane wzorce

Tabele strzałek integrują się bezpośrednio z popularnymi bibliotekami danych Python. Poniższe przykłady pokazują, jak przekazywać dane Arrow do panda, polarzy, DuckDB oraz formatów plików bez kopiowania danych.

Wczytaj wyniki do 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())

Załaduj wyniki do Polars

import polars as pl

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

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

Wyniki zapytań za pomocą DuckDB

DuckDB może zapytywać tabele strzałek bezpośrednio w SQL bez kopiowania danych. Ta możliwość jest przydatna, gdy potrzebujesz analizy w stylu SQL na zestawach wyników już w formacie 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())

Przesyłaj strumieniowo duże zbiory wyników do formatu Parquet

Dla dużych zbiorów wyników streamuj partie Arrow bezpośrednio do pliku Parquet, nie ładując całego zbioru danych do pamięci. ParquetWriter zapisuje każdą partię przyrostowo.

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

Eksport do innych formatów

PyArrow oferuje wbudowane mechanizmy zapisu do formatu CSV i formatu plików Arrow IPC (znanego również jako Feather V2). Pliki IPC Arrow zachowują dokładnie typy Arrow i są szybkie do odczytu.

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)

Mapowanie typu danych

Metody pobierania strzałek mapują typy Microsoft SQL na typy strzałek na poziomie C++.

Typ Microsoft SQL Typ strzałki
int, smallint, tinyint, bigint int32, int16, int8, int64
Float, real float64, float32
Dziesiętny, numeryczny decimal128
bit bool
Char, Varchar, Nchar, Nvarchar utf8
tekst, ntext large_utf8
binary, varbinary binary, large_binary
date date32
time time64[us]
datetime, datetime2, smalldatetime timestamp[us]
datetimeoffset timestamp[us, tz=UTC]
uniqueidentifier utf8 (ciąg pisany wielkimi literami)
xml utf8

Note

Sterownik konwertuje datetimeoffset typ na UTC, ponieważ kolumny strzałek wymagają stałej strefy czasowej. Sterownik normalizuje informacje o strefach czasowych dla poszczególnych komórek z Microsoft SQL do UTC podczas konwersji.

Typ sql_variant nie jest obsługiwany przez metody pobierania danych Arrow i powoduje zgłoszenie wyjątku informującego o nieobsługiwanym typie danych. Użyj standardowego fetchone(), fetchmany(), lub fetchall() do zapytań zwracających sql_variant kolumny.

Zagadnienia dotyczące wydajności

Metody pobierania strzałek są najszybsze do analiz i operacji z danymi zbiorczymi, natomiast standardowe metody kursora lepiej sprawdzają się w wzorcach transakcyjnych z małymi zbiorami wyników.

Kiedy używać Arrow, a kiedy standardowego fetch

Scenario Zalecane podejście
Pobierz kilka wierszy do wyświetlenia fetchone() / fetchall()
Ładuj dane do pand lub polarów cursor.arrow()
Przetwarzaj duże zbiory danych w blokach cursor.arrow_reader()
Wyszukiwania w pojedynczym wierszu lub małe zbiory wyników fetchone() / fetchval()
Potoki analityki lub agregacji cursor.arrow() + Polars/DuckDB
Zapisz wyniki w formacie Parquet lub Arrow IPC cursor.arrow_reader() + PyArrow I/O

Zarządzanie pamięcią dla dużych zbiorów danych

W przypadku zbiorów wyników, które mogą być większe niż dostępna pamięć, użyj arrow_reader(), ustawiając batch_size na rozsądną wartość.

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

Dostosuj rozmiar partii

Parametr ten batch_size kontroluje, ile wierszy jest pobieranych w każdej partii. Optymalny rozmiar zależy od szerokości wiersza i dostępnej pamięci. Szersze wiersze z dużymi kolumnami, takimi jak nvarchar(max) czy varbinary(max), korzystają z mniejszych rozmiarów partii, podczas gdy wąskie wiersze korzystają z większych.

  • Domyślne (8192): Dobra równowaga dla większości obciążeń.
  • Mniejsze (1000-5000): Używa się do szerokich tabel z dużymi kolumnami.
  • Większe (50000-100000): Zastosowanie do wąskich tabel lub gdy przepustowość ma większe znaczenie niż pamięć.
# 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)