mssql-pythonを用いたカスタム型コンバータ

デフォルトでは、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_DECIMAL 10進数、マネー、小額通貨の各列を変換します。なぜなら、これら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つのコンバーターを次の順番で選択します。

  1. コンバーターは列の整数ODBC SQL型コードを登録していました。

  2. コンバーターはcursor.description年にPythonタイプとして登録されました。

  3. コンバーターは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