デフォルトでは、mssql-pythonドライバーはMicrosoftのSQLデータ型を適切なPython型に変換します(データ型マッピングを参照)。 出力変換システムでは、特定のSQLタイプに対してこの動作をオーバーライドでき、以下が可能になります:
- カスタム型変換(たとえば、Decimal ではなく金額型を float に変換するなど)
- サードパーティライブラリとの統合(例えば、Shapelyオブジェクトへの空間データ)
- ビジネス固有の書式設定(例:日付からカスタム文字列形式への変換)
レジスタ出力コンバータ
add_output_converter()を使ってコンバーター関数を登録してください。 キーはcursor.descriptionのtype_codeに合致するPython型か、mssql_python.SQL_DECIMALのような整数ODBC SQL型コードのいずれかです。
import mssql_python
from datetime import datetime
def my_datetime_converter(value):
"""Convert datetime to a custom display string."""
if value is None:
return None
return value.strftime("%B %d, %Y at %I:%M %p")
conn = mssql_python.connect(connection_string)
conn.add_output_converter(datetime, my_datetime_converter)
cursor = conn.cursor()
cursor.execute("SELECT CAST('2026-07-16T10:30:00' AS DATETIME2)")
print(cursor.fetchval()) # July 16, 2026 at 10:30 AM
PythonタイプのキーとSQLタイプのコードキーのどちらかを選びましょう
この2つのキーフォームは、列の選択方法に違いがあります。
Python型キーは、そのPython型にマッピングされるすべてのSQL型に適用されます。
Decimalの変換器を登録すると、十進、数値、マネー、スモールマネーの各列が変換されます。整数の SQL 型コードキーは、列が返す ODBC 型コードと一致します。これはより限定的ですが、必ずしも単一の SQL 型であるとは限りません。
SQL_NUMERIC数値 列のみを 変換します。SQL_DECIMAL10進数、マネー、小額通貨の各列を変換します。なぜなら、これら3つが同じコードを報告しているからです。
Pythonタイプのキーが望むより広い場合や、整数キーを使うpyodbcからのコードを移植する場合は整数キーを使いましょう。 1つのコンバーターでSQL型ファミリー全体に対応させたい場合は、Python の型キーを使用します。
import mssql_python
def tag_decimal(value):
return f"D:{value}"
conn.add_output_converter(mssql_python.SQL_DECIMAL, tag_decimal)
cursor.execute("""
SELECT CAST(12.34 AS decimal(10,2)),
CAST(56.78 AS numeric(10,2)),
CAST(90.12 AS money)
""")
print(cursor.fetchone()) # ('D:12.34', Decimal('56.78'), 'D:90.1200')
コンバーターの解決順序
各列に対して、運転士は最大1つのコンバーターを次の順番で選択します。
コンバーターは列の整数ODBC SQL型コードを登録していました。
コンバーターは
cursor.description年にPythonタイプとして登録されました。コンバーターは
SQL_WVARCHARを登録しましたが、カラムのPythonタイプがstrまたはbytesの場合のみです。
変換関数シグネチャ
Python 型キーを使用してコンバーターを登録する場合、add_output_converter() に渡す型は、以下のいずれかの Python 型と一致している必要があります。
コンバーター受信列は、ドライバーがあなたの関数に何を渡すかを示しています:
cursor.description type_code |
コンバータは受信します | Microsoft SQL タイプ |
|---|---|---|
int |
int |
tinyint、smallint、int、bigint |
float |
float |
real、float |
decimal.Decimal |
Decimal |
decimal、numeric、money、smallmoney |
str |
bytes (UTF-16LEエンコード) |
char、varchar、nchar、nvarchar、xml |
datetime.datetime |
datetime |
datetime、datetime2、smalldatetime |
datetime.date |
date |
date |
bytes |
bytes |
varbinary、binary、geography、geometry |
bool |
bool |
bit |
Note
文字列型コンバータ(str キー)は、デコード済みの Python 文字列ではなく、UTF-16LE エンコーディングの生の bytes を受け取ります。 その他のタイプはすべて、すでに変換済みのPythonオブジェクトを受け取ります。
from decimal import Decimal
def decimal_converter(value: Decimal | None) -> float | None:
if value is None:
return None
# value is a Decimal object for numeric/money types
return float(value)
コンバーターの管理
これらの方法を使って、接続部に既に登録されているコンバーターを点検または取り外してください。
既存のコンバーターを入手してください
現在登録されている型の変換関数を取得します:
converter = conn.get_output_converter(str)
if converter:
print(f"Converter registered: {converter}")
else:
print("Using default conversion")
コンバーターを取り外してください
コンバーターを登録解除して、ドライバーがそのタイプのデフォルト変換に戻る:
conn.remove_output_converter(str)
# str columns now use default conversion
すべてのコンバーターをクリア
すべての登録済みコンバータをMicrosoft SQLのデフォルトのタイプマッピングにリセットしてください:
conn.clear_output_converters()
# All types now use default conversion
一般的なコンバーターパターン
これらの例は、最も頻繁に必要とされる変換器の機能を示しています。
VARCHARを大文字に変換する
ストリングタイプコンバータはUTF-16LEエンコーディングの生バイトを受け取ります:
def uppercase_converter(value):
if value is None:
return None
return value.decode('utf-16-le').upper()
conn.add_output_converter(str, uppercase_converter)
cursor = conn.cursor()
cursor.execute("SELECT 'hello world'")
print(cursor.fetchval()) # HELLO WORLD
マネーをフロートに変換する
デフォルトでは、ドライバーは decimal.Decimalとして10進数/数名型を返します。 浮動に変換する:
from decimal import Decimal
def money_to_float(value):
if value is None:
return None
return float(value)
conn.add_output_converter(Decimal, money_to_float)
cursor = conn.cursor()
cursor.execute("SELECT CAST(19.99 AS MONEY)")
result = cursor.fetchval()
print(type(result)) # <class 'float'>
Parse JSON data
Microsoft SQL ServerはJSONをテキストとして保存できます。 Pythonオブジェクトとして自動的に解析:
import json
def json_converter(value):
if value is None:
return None
text = value.decode('utf-16-le')
try:
return json.loads(text)
except json.JSONDecodeError:
return text # Return as string if not valid JSON
conn.add_output_converter(str, json_converter)
cursor = conn.cursor()
cursor.execute("SELECT '{\"name\": \"Widget\", \"price\": 19.99}'")
data = cursor.fetchval()
print(data['name']) # Widget
Caution
この例はすべての文字列値(nvarchar、varchar、xml)を変換します。 JSON解析はすべての文字列列で試みられます。 実際には、JSONの解析をブランケットコンバーターとしてではなく選択的に適用してください。
カスタム日付と時刻の書式設定
datetimeをISO文字列形式に変換する:
from datetime import datetime
def datetime_to_iso(value):
if value is None:
return None
return value.isoformat()
conn.add_output_converter(datetime, datetime_to_iso)
cursor = conn.cursor()
cursor.execute("SELECT TOP 1 ModifiedDate FROM Production.Product")
print(cursor.fetchval()) # 2014-02-08T10:01:36.827000
セキュリティに関する考慮事項
Caution
出力コンバータを登録すると、提供する関数は すべての一致するデータベース値に対して動作します。
- 信頼できるコードからのみ、コンバーターを登録してください。
- ユーザーの入力からコンバーター機能を受け入れてはいけません。
- 悪意のあるコンバーターは任意のコードを実行したり、データをリークしたりすることがあります。
# DANGEROUS - never do this
def unsafe_example(user_converter_code):
converter_func = eval(user_converter_code) # Security risk!
conn.add_output_converter(str, converter_func)
例:空間データ統合
空間データをShapelyライブラリと統合する:
from shapely import wkb
def geometry_converter(value):
"""Convert WKB binary to Shapely geometry object."""
if value is None:
return None
return wkb.loads(value)
conn.add_output_converter(bytes, geometry_converter)
# Query must use STAsBinary() to get standard WKB format
cursor = conn.cursor()
cursor.execute(
"SELECT SpatialLocation.STAsBinary() FROM Person.Address "
"WHERE SpatialLocation IS NOT NULL AND City = 'Seattle'"
)
for row in cursor:
shape = row[0]
print(f"Point: ({shape.x:.4f}, {shape.y:.4f})")
Note
bytesコンバータの登録はすべてのバイナリカラム(varbinary、geography、geometry)に影響を与えます。 空間クエリの後に clear_output_converters() を使い、非空間的なバイナリデータもクエリする場合に使います。
例:カスタム行書式
データクラス変換ツールの作成:
from dataclasses import dataclass
@dataclass
class Product:
id: int
name: str
price: float
def fetch_products_as_dataclass(cursor):
"""Convert rows to Product dataclass instances."""
cursor.execute(
"SELECT ProductID, Name, ListPrice FROM Production.Product "
"WHERE ListPrice > 0"
)
results = []
for row in cursor:
results.append(Product(
id=row.ProductID,
name=row.Name,
price=float(row.ListPrice)
))
return results
products = fetch_products_as_dataclass(cursor)
for p in products[:5]:
print(f"{p.name}: ${p.price:.2f}")
コンバータースコープと寿命
- 接続単位: ドライバーはコンバーターをグローバルではなく、接続ごとに登録します。
- 永続的:コンバーターは取り外したり接続を切断するまで有効のままです。
- 継承されていない:新しい接続は他の接続からコンバーターを引き継ぎません。
conn1 = mssql_python.connect(connection_string)
conn1.add_output_converter(str, uppercase_converter)
conn2 = mssql_python.connect(connection_string)
# conn2 does NOT have the converter registered
パフォーマンスに関する考慮事項
- ドライバーは登録された型 のすべての値 に対してコンバータを呼び出します。
- コンバーター機能の効率を保ちましょう。
- 大量クエリの場合は、変換が必要かどうかを考慮してください。
- コンバータがスループットに影響を与える場合のプロファイル性能。
import time
def slow_converter(value):
time.sleep(1) # Avoid time consuming routines like this - adds 1 second for each value processed!
return str(value) if value else None