mssql-python ile özel tip dönüştürücüler

Varsayılan olarak, mssql-python sürücüsü Microsoft SQL veri tiplerini uygun Python tiplerine dönüştürür (bkz. Veri tipi eşlemeleri). Çıkış dönüştürücü sistemi, belirli SQL türleri için bu davranışı geçersiz hale getirmenize olanak tanır ve şunları etkinleştirir:

  • Özel tür dönüşümleri (örneğin, Decimal yerine money türünün float’a dönüştürülmesi)
  • Üçüncü taraf kütüphanelerle entegrasyon (örneğin, mekânsal verilerin Shapely nesnelerine dönüştürülmesi)
  • İşletmeye özel biçimlendirme (örneğin, tarihlerin özel dizge biçimlerine dönüştürülmesi)

Çıktı dönüştürücülerini kaydedin

Bir dönüştürücü fonksiyonunu kaydetmek için kullanılır add_output_converter() . Anahtar, cursor.description içindeki type_code ile eşleşen bir Python türüdür:

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

Dönüştürücü fonksiyon imzası

Sürücü, her sütun için cursor.description içindeki Python türünü eşleştirerek dönüştürücüleri bulur. add_output_converter() öğesine geçirdiğiniz tür, şu Python türlerinden biriyle eşleşmelidir:

cursor.description type_code Dönüştürücü alır Microsoft SQL türleri
int int tinyint, smallint, int, bigint
float float real, float
decimal.Decimal Decimal decimal, numeric, money, smallmoney
str bytes (UTF-16LE ile kodlanmış) 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

Dizi tipi dönüştürücüler (strkey) UTF-16LE kodlamasında ham bytes alır, çözülmüş Python dizelerini değil. Diğer tüm türler zaten dönüştürülmüş Python nesnesini alır.

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)

Dönüştürücüleri yönetin

Bu yöntemleri kullanarak bir bağlantıda zaten kayıtlı olan dönüştürücüleri inceleyin veya çıkarın.

Mevcut dönüştürücüyü alın

Şu anda bir tür için kayıtlı dönüştürücü fonksiyonunu alın:

converter = conn.get_output_converter(str)
if converter:
    print(f"Converter registered: {converter}")
else:
    print("Using default conversion")

Bir dönüştürücüyü çıkarın

Bir dönüştürücüyü kaydından çıkarın ki sürücü o tür için varsayılan dönüşüme geri dönsün:

conn.remove_output_converter(str)
# str columns now use default conversion

Tüm dönüştürücüleri temizle

Tüm kayıtlı dönüştürücüleri Microsoft SQL'nin varsayılan tür eşlemelerini kullanacak şekilde sıfırlayın:

conn.clear_output_converters()
# All types now use default conversion

Yaygın dönüştürücü desenleri

Bu örnekler, en sık ihtiyaç duyulan dönüştürücü fonksiyonlarını gösterir.

VARCHAR'ı büyük harfe çevir

Dizi tipi dönüştürücüler UTF-16LE kodlamasında ham baytlar alır:

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

Parayı kayan noktaya dönüştür

Varsayılan olarak, sürücü DECIMAL/NUMERIC tiplerini şu şekilde decimal.Decimaldöndürür. Kayan noktalı sayıya dönüştürün:

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'>

JSON verilerini ayrıştır

Microsoft SQL Server JSON'u metin olarak depolayabilir. Otomatik olarak Python nesnelerine ayrıştırın:

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

Bu örnek, TÜM dizi değerlerini (nvarchar, varchar, xml) dönüştürür. JSON ayrıştırma her dize sütununda denenir. Pratikte, JSON ayrıştırmasını kapsamlı dönüştürücü yerine seçici olarak uygulayın.

Özel tarih saati formatlama

Datetime'ı ISO dizisi formatına dönüştürün:

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

Güvenlik konuları

Caution

Bir çıkış dönüştürücü kaydettiğinizde, sağladığınız fonksiyon her eşleşen veritabanı değerinde çalışır.

  • Dönüştürücüleri yalnızca güvenilir koddan kaydedin.
  • Kullanıcı girişinden dönüştürücü fonksiyonlarını asla kabul etmeyin.
  • Kötü niyetli dönüştürücüler rastgele kod çalıştırabilir veya veri sızdırabilir.
# 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)

Örnek: Mekansal veri entegrasyonu

Mekânsal verileri Shapely kütüphanesi ile entegre edin:

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

Bir bytes dönüştürücü kaydetmek TÜM ikili sütunları (varbinary, coğrafya, geometri) etkiler. Uzamsal olmayan ikili verileri de sorguluyorsanız, uzamsal sorgulardan sonra clear_output_converters() kullanın.

Örnek: Özel Satır Biçimlendirmesi

Bir veri sınıfı dönüştürücü oluşturun:

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

Dönüştürücü kapsamı ve ömrü

  • Bağlantı kapsamında: Sürücü, dönüştürücüleri genel olarak değil, her bağlantı için kaydeder.
  • Kalıcı: Dönüştürücüler, siz onları kaldırana veya bağlantıyı kapatana kadar etkin kalır.
  • Miras alınmamış: Yeni bağlantılar diğer bağlantılardan dönüştürücüleri devralmaz.
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

Performansla ilgili dikkat edilmesi gerekenler

  • Sürücü, kayıtlı tipin her değeri için dönüştürücü çağırır.
  • Dönüştürücü fonksiyonlarını verimli tutun.
  • Yüksek hacimli sorgular için, dönüşümün gerekli olup olmadığını düşünün.
  • Dönüştürücüler verimi etkiliyorsa performans profilini çıkarın.
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