PythonでSQL Serverに大量のデータを送り込むとき、executemany()のループで何分も待たされた経験はないでしょうか。
Microsoftが開発する公式Pythonドライバ「mssql-python」は、2025年11月のGA(正式リリース)から半年で6回のメジャーアップデートを重ね、Bulk Copy(一括挿入)とApache Arrow(ゼロコピーデータ取得)という2つの注目機能を搭載しました。
この記事でわかること
- mssql-pythonの概要とpyodbcとの違い
- Bulk Copyで大量行を高速に挿入する方法
- Apache Arrowでpandas・Polarsへ効率的にデータを渡す方法
- v1.6で改善されたマルチスレッド性能
mssql-pythonとは
mssql-pythonは、MicrosoftがSQL Server・Azure SQL Database・Microsoft Fabric向けに開発している公式Pythonドライバです。従来のPython×SQL Serverの定番はpyodbcでしたが、mssql-pythonはC++とpybind11で構築された独自アーキテクチャ(DDBC)を採用し、ODBCドライバの別途インストールなしで動作します。
pip install mssql-pythonの1コマンドで導入でき、DB API 2.0に準拠しているため、既存コードからの移行もスムーズです。Entra ID認証やコネクションプーリングも標準で備えています。2025年11月のv1.0リリースから2026年4月のv1.6まで、月1回以上のペースで機能追加が続いています。
Bulk Copyで数百万行を一括挿入する
v1.4で追加されたBulk Copy(BCP)は、SQL ServerのネイティブなTDS一括挿入プロトコルを使って大量のデータを送り込む機能です。通常のexecutemany()では1行ごとにSQLのパース・プランコンパイル・ラウンドトリップが発生しますが、Bulk Copyはこれらをすべて省略します。
使い方は3行で完結します。
import mssql_python
conn = mssql_python.connect(
"SERVER=myserver;DATABASE=mydb;"
"UID=user;PWD=pass;Encrypt=yes"
)
cursor = conn.cursor()
rows = [
(1, "Alice", "alice@example.com"),
(2, "Bob", "bob@example.com"),
]
result = cursor.bulkcopy("dbo.Users", rows)
print(f"{result['rows_copied']}行を"
f"{result['elapsed_time']:.2f}秒で挿入")
バッチサイズ、テーブルロック、制約チェック、トリガー発火の制御など、.NETのSqlBulkCopyと同等のオプションも指定できます。内部ではRustで実装されたコアライブラリ(mssql-py-core)がTDSプロトコルを処理しており、Pythonのオーバーヘッドを最小限に抑えています。
Apache Arrowでゼロコピーのデータ取得
v1.5では、SQL Serverの結果セットをApache Arrow形式で直接取得するAPIが追加されました。従来のfetchall()はすべての値をPythonオブジェクトに変換するため、大量データではボトルネックになります。Arrow APIはC Data Interfaceを使い、C++レイヤーからPyArrowへゼロコピーで受け渡します。カラムナ形式のまま一貫して処理されるため、中間コピーが発生しません。
3つのメソッドが用途に応じて使い分けられます。
cursor.arrow()— 結果セット全体をPyArrow Tableとして取得し、pandasやPolarsへそのまま変換できるcursor.arrow_batch(batch_size=10000)— 指定サイズのRecordBatchを1つずつ取得し、メモリを細かく制御したい場合に使うcursor.arrow_reader(batch_size=8192)— ストリーミング用のRecordBatchReaderを返し、Parquetファイルへの直接書き出しに向いている
cursor.execute("SELECT * FROM Sales.SalesOrderDetail")
table = cursor.arrow()
df = table.to_pandas() # ゼロコピーでpandasに変換
SQL Serverの各データ型(INT→int32、BIGINT→int64、DECIMAL→decimal128など)はすべて適切なArrow型にマッピングされます。この機能はコミュニティからのコントリビューションで実現しました。
v1.5のその他の新機能
Arrow対応と同時に、v1.5ではsql_variant型のサポートとUUIDのネイティブ返却が追加されています。
sql_variant型は、1つのカラムに異なるデータ型の値を格納するSQL Server独自の型です。設定テーブルやEAV(Entity-Attribute-Value)パターンで使われます。mssql-pythonはsql_variantの内部型タグを読み取り、Pythonの適切な型(int、float、str、datetime.dateなど)で自動的に返します。
UNIQUEIDENTIFIER列は、v1.5からデフォルトでPythonのuuid.UUIDオブジェクトとして返されるようになりました。pyodbcでは文字列で返されるため、手動変換が必要でした。既存コードとの互換性を保ちたい場合は、接続時にnative_uuid=Falseを指定すれば従来の文字列形式に戻せます。
v1.6でマルチスレッド性能が向上
2026年4月24日にリリースされたv1.6では、SQLDriverConnectとSQLDisconnectの実行中にPythonのGIL(Global Interpreter Lock)を解放する改修が入りました。DNS解決、TCPハンドシェイク、TLSネゴシエーション、認証といったブロッキングI/O中に他のスレッドが止まらなくなります。10スレッドの同時接続テストでは、従来比約5.7倍の高速化が確認されています。
セキュリティ面では、setup_logging()のログファイルパスにディレクトリトラバーサル対策が追加されました。../を含む相対パスは拒否され、正規化されたパスのみが受け入れられます。
pyodbcから移行するには
mssql-pythonはDB API 2.0に準拠しているため、import文と接続文字列を変更するだけで動くケースが多いです。ODBCドライバの別途インストールが不要になる点は、DockerコンテナやCI/CD環境で特に恩恵が大きいでしょう。
注意すべき違いとして、前述のUUID型のデフォルト挙動があります。文字列を前提としたコードにはnative_uuid=Falseオプションで対応できます。Bulk CopyやArrow APIはpyodbcにない機能なので、移行後に段階的に導入するのが現実的です。
今後のロードマップ
公式ロードマップ(参考)では、asyncioによる非同期クエリ実行、SQL Server 2025のベクター型サポート、テーブル値パラメータ(TVP)対応が予定されています。半年で1.0から1.6まで到達した開発ペースを踏まえると、これらの機能追加も近い時期に期待できます。