mssql-python 驅動程式提供 Apache Arrow 擷取方法,用於從 Microsoft SQL 和 Azure SQL Database 進行高效能欄位資料檢索。
Apache Arrow 是一個跨語言的記憶體欄位資料開發平台。 驅動程式直接將 ODBC 結果集轉換為 C++ 的 Arrow 格式,跳過 Python 物件建立以提升效能。
Arrow 整合可實現:
- 零複製資料傳輸 至 Polars、Pandas 和 DuckDB。 「零複製」意指資料會停留在單一的記憶體緩衝區,驅動程式會寫入並直接讀取函式庫,因此不會有列重複成中間的 Python 物件。
- 串流結果會直接通過
RecordBatchReader,但不會把所有資料載入記憶體。 - 欄位資料格式,非常適合分析與機器學習工作負載。
- 相較於逐列 Python 物件建立,記憶體使用量更低。
游標方法
若要使用 Arrow 的 fetch 方法,則需要 pyarrow 套件。 使用 pip install pyarrow 安裝。 如果 pyarrow 沒有安裝,呼叫任何 Arrow 方法都會產生一個 ImportError。
mssql-python 驅動程式在游標物件中新增三種 Arrow 資料存取的方法。 這三種方法都會將 ODBC 結果集轉換為驅動程式的 C++ 層中的 Arrow 格式,避免建立中間的 Python 物件。
-
arrow()將整個結果集以一個記憶體內資料表回傳。 最簡單好用。 -
arrow_batch()一次回傳一列,讓你能手動控制迴圈。 -
arrow_reader()回傳一個自動產生批次的迭代器。 最適合用於串流大量結果。
使用 cursor.arrow(batch_size=8192)
將整個結果集擷取為單一的 pyarrow.Table。 此方法最簡單,且當完整結果集能放入記憶體時運作良好。
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
如果你的 連接字串 使用Authentication=ActiveDirectoryDefault,驅動程式會使用 DefaultAzureCredential,並依序嘗試多個憑證提供者。 第一次連線可能會比較慢,因為 SDK 會一直走鏈條直到找到可用的供應商。 在生產環境中,如果您知道您的環境使用哪一種憑證類型,請直接指定該憑證類型(例如,針對受控識別可指定 ActiveDirectoryMSI),以避免逐一嘗試整個憑證鏈。 如需詳細資訊,請參閱 Microsoft Entra 驗證。
使用 cursor.arrow_batch(batch_size=8192)
擷取單一pyarrow.RecordBatch,其中最多包含batch_size列。 這種方法用於需要細緻控制一次擷取幾列的自訂批次處理迴圈。
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")
使用 cursor.arrow_reader(batch_size=8192)
回傳一個 pyarrow.RecordBatchReader,該物件會產生 RecordBatch 物件,直到結果集耗盡為止。 此方法是處理大型結果集時記憶體效率最高的選擇。
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")
常見模式
箭頭表格可直接整合於熱門的 Python 資料庫。 以下範例說明如何將 Arrow 資料傳給 pandas、Polars、DuckDB 及檔案格式,且不複製資料。
將結果載入 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())
將結果載入 Polars
import polars as pl
cursor.execute("SELECT * FROM Production.Product")
table = cursor.arrow()
df = pl.from_arrow(table)
print(df)
DuckDB 查詢結果
DuckDB 可以直接在 SQL 中查詢 Arrow 資料表,無需複製資料。 當你需要對已為 Arrow 格式的結果集進行 SQL 式分析時,這項功能非常有用。
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())
將大型結果集串流至 Parquet
對於大型結果集,可以直接將 Arrow 批次串流到 Parquet 檔案,無需將整個資料集載入記憶體。
ParquetWriter 會逐步寫入每個批次。
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()
匯出至其他格式
PyArrow 內建 CSV 及 Arrow IPC 檔案格式(亦稱為 Feather V2)的寫入程式。 Arrow IPC 檔案能完整保留 Arrow 類型,且回讀速度快。
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)
數據類型對應
Arrow 的擷取方法將 Microsoft SQL 類型映射到 C++ 層級的 Arrow 類型。
| Microsoft SQL 類型 | 箭頭類型 |
|---|---|
| int、smallint、tinyint、bigint |
int32、int16、int8、int64 |
| float、real |
float64、float32 |
| 十進位、 數字 | decimal128 |
| 位元 | bool |
| 查爾, 瓦爾查爾, 恩查爾, 恩瓦爾查爾 | utf8 |
| 文字, ntext | large_utf8 |
| 二進位、可變長度二進位 |
binary、large_binary |
| date | date32 |
| time | time64[us] |
| 約會時間, 約會時間2, 小約會時間 | timestamp[us] |
| datetimeoffset | timestamp[us, tz=UTC] |
| uniqueidentifier |
utf8 (大寫字串) |
| xml | utf8 |
Note
驅動程式會 datetimeoffset 將類型轉換成 UTC,因為箭頭欄位需要固定的時區。 驅動程式在轉換時將每個單元時區資訊從 Microsoft SQL 標準化為 UTC。
Arrow 擷取方法不支援此 sql_variant 型別,且會觸發不支援的資料型別例外。 對於會傳回 sql_variant 個資料欄的查詢,請使用標準 fetchone()、fetchmany() 或 fetchall()。
效能考量
箭頭擷取方法在分析與大量資料操作中速度最快,而標準游標方法則更適合處理具有小型結果集的交易模式。
什麼時候該用箭頭 還是標準抓球
| Scenario | 建議方法 |
|---|---|
| 擷取幾列資料供顯示 | fetchone() / fetchall() |
| 將資料載入 Pandas 或 Polar | cursor.arrow() |
| 將大型資料集分段處理 | cursor.arrow_reader() |
| 單列查詢或小型結果集 | fetchone() / fetchval() |
| 分析或彙整流程 |
cursor.arrow() + Polars/DuckDB |
| 將結果寫入Parquet或Arrow IPC |
cursor.arrow_reader() + PyArrow I/O |
大型資料集的記憶體管理
若結果集可能超出可用記憶體,請搭配合理的 batch_size 使用 arrow_reader()。
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")
調整批次大小
參數 batch_size 控制每批次擷取的列數。 最佳大小取決於你的列寬度和可用記憶體。 包含 nvarchar(max) 或 varbinary(max) 等大型資料行的寬資料列較適合較小的批次大小,而窄資料列則較適合較大的批次大小。
- 預設(8192):大多數工作負載的平衡良好。
- 較小(1000-5000):用於欄位較寬的寬表格。
- 較大(50000-100000):用於窄表或吞吐量比記憶體更重要時。
# 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)