mssql-python ile sorgu, veri ve işlem sorunlarını sorun giderme

Bu makaleyi sürücüyle ilgili sorgu yürütme, veri türü, performans, işlem ve toplu kopyalama sorunlarını mssql-python teşhis etmek için kullanın.

Sorgu yürütme sorunları

Tablo veya nesne bulunamadı

Belirti -leri:

ProgrammingError: [42S02] (208) Invalid object name 'TableName'.

Olası nedenler ve çözümler:

  • Yanlış veritabanı bağlamı

    cursor.execute("SELECT DB_NAME()")
    print(cursor.fetchone()[0])
    
  • Belirtilmemiş şema

    cursor.execute("SELECT * FROM dbo.TableName")
    
  • Tablo yok

    cursor.execute("""
        SELECT TABLE_NAME
        FROM INFORMATION_SCHEMA.TABLES
        WHERE TABLE_NAME = 'TableName'
    """)
    

Söz dizimi hatası

Belirti -leri:

ProgrammingError: [42000] (102) Incorrect syntax near '...'.

Çözümler:

  1. Sözdizimi doğrulamak için SQL ifadesini SQL Server Management Studio (SSMS)'de test edin.

  2. Dizi interpolasyonu yerine parametrizlenmiş bir sorgu kullanın:

    # Don't use string interpolation for query parameters.
    cursor.execute(f"SELECT * FROM Production.Product WHERE Name = '{name}'")
    
    # Use parameters.
    cursor.execute(
        "SELECT * FROM Production.Product WHERE Name = %(name)s",
        {"name": name},
    )
    

Parametre hataları

Belirti -leri:

ProgrammingError: [07001] Wrong number of parameters

Çözümler:

  1. Yer tutucuları ve parametreleri say. Sayılar aynı olmalı.

  2. Doğru parametre stilini seçin:

    # Qmark style: positional parameters
    cursor.execute(
        "SELECT * FROM Production.Product "
        "WHERE ProductID = ? AND Name LIKE ?",
        (1, "Adjustable%"),
    )
    print(cursor.fetchone())
    
    # Pyformat style: named parameters
    cursor.execute(
        "SELECT * FROM Production.Product "
        "WHERE ProductID = %(id)s AND Name LIKE %(name)s",
        {"id": 1, "name": "Adjustable%"},
    )
    print(cursor.fetchone())
    

Veri tipi sorunları

Tarih saati dönüşüm hataları

Belirti -leri:

DataError: [22007] Invalid datetime format

Çözüm:

Dizeler yerine Python datetime nesneleri kullanın.

from datetime import datetime

cursor.execute("CREATE TABLE #Events (EventDate DATETIME)")

# This value raises an error because the date is invalid.
try:
    cursor.execute(
        "INSERT INTO #Events (EventDate) VALUES (%(event_date)s)",
        {"event_date": "2024-13-45"},
    )
except Exception as e:
    print(f"Expected error: {e}")

# Use a Python datetime object.
cursor.execute(
    "INSERT INTO #Events (EventDate) VALUES (%(event_date)s)",
    {"event_date": datetime(2024, 3, 15)},
)
cursor.execute("SELECT EventDate FROM #Events")
print(cursor.fetchone())

Ondalık hassasiyet sorunları

Belirti -leri:

Sayılar kısaltılmış veya yanlış yuvarlanmış gibi görünür.

Çözüm:

Kesin sayısal değerler için decimal.Decimal kullanın:

from decimal import Decimal

cursor.execute("CREATE TABLE #PriceDemo (ListPrice DECIMAL(10, 2))")
cursor.execute(
    "INSERT INTO #PriceDemo (ListPrice) VALUES (%(list_price)s)",
    {"list_price": Decimal("19.99")},
)

Unicode kodlama sorunları

Belirti -leri:

Özel karakterler karışık görünür veya hatalara yol açar.

Çözümler:

  1. Veritabanınızdaki Unicode verileri için nvarchar sütunlarını kullanın.

  2. Dizgeleri doğrudan iletin. Sürücü kodlamayı yönetir:

    cursor.execute("CREATE TABLE #UnicodeDemo (Name NVARCHAR(50))")
    cursor.execute(
        "INSERT INTO #UnicodeDemo (Name) VALUES (%(name)s)",
        {"name": "日本語"},
    )
    cursor.execute("SELECT Name FROM #UnicodeDemo")
    print(cursor.fetchone())
    

Performans sorunları

Yavaş sorgu yürütme

Olası nedenler ve çözümler:

  • Eksik indeksler: SSMS'de sorgu yürütme planını kontrol edin.

  • Büyük sonuç kümeleri: Yerine fetchall()kullanınfetchmany():

    cursor.arraysize = 1000
    while True:
        rows = cursor.fetchmany()
        if not rows:
            break
        process_rows(rows)
    
  • Bağlantı havuzlama devre dışı bırakıldı: Havuzlama etkinleştirin:

    import mssql_python
    
    mssql_python.pooling(max_size=20, idle_timeout=300)
    

Büyük sonuçlarda bellek sorunları

Belirti -leri:

Python sürecinin belleği tükeniyor.

Çözümler:

  1. Tüm satırları belleğe yüklemek yerine sonuçları akış yapın.

    cursor.execute("SELECT * FROM LargeTable")
    for row in cursor:
        process_row(row)
    
  2. Sunucu tarafı sayfalama kullanın.

    page_size = 1000
    offset = 0
    
    while True:
        cursor.execute(
            "SELECT * FROM LargeTable ORDER BY ID "
            "OFFSET ? ROWS FETCH NEXT ? ROWS ONLY",
            (offset, page_size),
        )
        rows = cursor.fetchall()
        if not rows:
            break
        process_rows(rows)
        offset += page_size
    

İşlem sorunları

Otomatik commit ile geçici tablo kapsamı

Bir işlem içinde oluşturduğunuz oturum geçici tabloları (#tablename) işlem geri çekildiğinde kaybolur. Bu davranış genellikle otomatik commit kapalı olduğunda karışıklığa yol açar ve bu varsayılan durumdur:

conn = mssql_python.connect(connection_string)
cursor = conn.cursor()

cursor.execute("CREATE TABLE #TempData (ID INT, Name NVARCHAR(50))")
cursor.execute("INSERT INTO #TempData VALUES (1, 'test')")

# An explicit rollback or an error removes #TempData.
conn.rollback()

# This statement fails with "Invalid object name '#TempData'".
cursor.execute("SELECT * FROM #TempData")

Geçici bir tablo oluşturduktan hemen sonra commit yapın veya otomatik commit modunu kullanın:

cursor.execute("CREATE TABLE #TempData (ID INT, Name NVARCHAR(50))")
conn.commit()

cursor.execute("INSERT INTO #TempData VALUES (1, 'test')")
conn.commit()

Otomatik commit modunu gerektiren DDL ifadeleri, örneğin CREATE DATABASE, açık bir işlem içinde başarısız olur. Bunları çalıştırmadan önce otomatik işlemeyi ayarlayın:

conn.autocommit = True
cursor.execute("CREATE DATABASE TestDB")
conn.autocommit = False

İşlem yapılmadı

Belirti -leri:

Bağlantı kapattıktan sonra veri değişiklikleri devam etmiyor.

Çözüm:

Varsayılan olan autocommit=False ile commit() çağrısını yapın:

cursor.execute("CREATE TABLE #Products (Name NVARCHAR(100))")
cursor.execute(
    "INSERT INTO #Products (Name) VALUES (%(name)s)",
    {"name": "Widget"},
)
conn.commit()

Alternatif olarak, otomatik commit modunu kullanın:

conn = mssql_python.connect(connection_string, autocommit=True)

Kilitlenme hataları

Belirti -leri:

OperationalError: [40001] (1205) Transaction ... was deadlocked on lock resources with another process

Çözüm:

Yeniden deneme mantığı anında hatayı yönetir, ancak tekrarlayan çıkmazlar bir tasarım sorunu olduğunu gösterir. Deadlock grafiğini yakalayın ve ifadeleri ile kilit türlerini analiz edin. Yaygın çözümler arasında şu değişiklikler yer alır:

  • Rakip işlemler aynı sırayla kilitler elde etmek için işlemleri yeniden sıralar.
  • İşlem kapsamını azaltın.
  • Kilit süresini azaltmak için uygun indeksler ekleyin.

Deadlock analizine dair ayrıntılı bir açıklama için Kilitlenmeler rehberine bakın. Azure SQL Veritabanı kullanıyorsanız, Kilitlenmeleri analiz etme ve önleme konusuna bakın.

Toplu yükleme sorunları

Toplu kopide sırasında kısıtlama ihlalleri

Belirti -leri:

RuntimeError: CHECK constraint ... Conflict occurred in database ...
RuntimeError: Cannot insert duplicate key ... violation of PRIMARY KEY constraint

Neden:

Toplu işlemdeki verileriniz, birincil anahtar, benzersiz, denetim veya yabancı anahtar kısıtlamaları gibi tablo kısıtlamalarını ihlal ediyor.

Çözüm:

Verileri yüklemeden önce doğrulayın. Büyük veri setleri için, veriyi bir aşamalama tablosuna yükleyin ve ardından hedefe birleştirin:

cursor.execute("CREATE TABLE ##Staging (ID INT, Name NVARCHAR(100))")
cursor.bulkcopy("##Staging", rows)

# Check for duplicate rows before the merge.
cursor.execute("""
    SELECT s.ID
    FROM ##Staging AS s
    INNER JOIN dbo.Target AS t
        ON s.ID = t.ID
""")
dupes = cursor.fetchall()
if dupes:
    print(f"Skipping {len(dupes)} duplicate rows")

# Insert rows that don't exist in the target.
cursor.execute("""
    INSERT INTO dbo.Target (ID, Name)
    SELECT s.ID, s.Name
    FROM ##Staging AS s
    WHERE NOT EXISTS (
        SELECT 1
        FROM dbo.Target AS t
        WHERE t.ID = s.ID
    )
""")
conn.commit()

Aşama tablolarıyla upsert desenleri için bkz. Veri yükleme ve hareket desenleri.

Sütun eşleme hataları

Belirti -leri:

RuntimeError: Bulk copy failure - column count mismatch

Neden:

Verinizdeki sütun sayısı, hedef tablo sütun sayısıyla eşleşmiyor ya da sütunlar yanlış sırada.

Çözüm:

Verilerinizin sırayla ve sayısal olarak tablo şemasıyla eşleştiğinden emin olun:

from decimal import Decimal

cursor.execute("""
    SELECT COLUMN_NAME, DATA_TYPE
    FROM INFORMATION_SCHEMA.COLUMNS
    WHERE TABLE_NAME = 'MyTable'
    ORDER BY ORDINAL_POSITION
""")
for col in cursor.fetchall():
    print(col)

rows = [
    (1, "Widget", Decimal("19.99")),
    (2, "Gadget", Decimal("29.99")),
]
cursor.bulkcopy("dbo.MyTable", rows)

Toplu kopyalama sırasında tip uyumsuzlukları

Belirti -leri:

Veri yüklenir, ancak değerler kısaltılmış, yuvarlatılmış veya yanlıştır.

Neden:

Python değerleri hedef sütun tipleriyle tam olarak eşleşmez. Yaygın örnekler arasında, decimal sütunlarına yüklenen ve hassasiyet kaybına yol açabilen float değerler ile sabit uzunluktaki sütunlara yüklenen gereğinden uzun dizeler yer alır.

Çözüm:

Şemanıza uyan Python türlerini kullanın:

from decimal import Decimal

rows = [
    # Use Decimal for decimal and numeric columns.
    (1, "Widget", Decimal("19.99")),
    # Avoid float values because they can lose precision.
    # (1, "Widget", 19.99),
]
cursor.bulkcopy("dbo.Products", rows)

NumPy tip bağlanma hataları

Belirti -leri:

NumPy tamsayı veya float tipleri kullandığınızda parametreler sessizce başarısız olur veya veri tipi hatalarını artırır.

Neden:

numpy.int64 ve numpy.int32 gibi NumPy türleri, NumPy 2.x'te isinstance(x, int) denetimini geçmez. Sürücünün tip çıkarımı onları tanımaz, bu da beklenmedik davranışlara yol açar.

Çözüm:

NumPy değerlerini bağlamadan önce yerel Python tiplerine dönüştürün:

import numpy as np

cursor.execute(
    "SELECT * FROM Production.Product WHERE ProductID = %(product_id)s",
    {"product_id": int(np.int64(42))},
)

for _, row in df.iterrows():
    cursor.execute(
        "INSERT INTO #Orders (ProductID, Qty) "
        "VALUES (%(product_id)s, %(qty)s)",
        {
            "product_id": int(row["ProductID"]),
            "qty": int(row["Qty"]),
        },
    )

Daha büyük veri setleri için Arrow veya pandas entegrasyon yollarını kullanın. Bu yollar tip dönüşümünü dahili olarak yönetir.

Geçici tablolarla toplu kopyalama

Belirti -leri:

cursor.bulkcopy("#TempTable", data), RuntimeError: Invalid object name '#TempTable' yükseltir.

Neden:

bulkcopy() meta veri arama sınırlamaları nedeniyle oturum geçici tablolarını (#tablename) çözemiyor. Genel geçici tablolar (##tablename) ve kalıcı tablolar çalışır.

Çözüm:

Küresel bir geçici tablo veya normal bir aşama tablosu kullanın:

# A global temp table is visible to all sessions and is dropped
# when the last session disconnects.
cursor.execute("CREATE TABLE ##Staging (ID INT, Name NVARCHAR(50))")
cursor.bulkcopy("##Staging", rows)

# Alternatively, use a permanent staging table.
cursor.execute("CREATE TABLE dbo.Staging (ID INT, Name NVARCHAR(50))")
cursor.bulkcopy("dbo.Staging", rows)

Oturum geçici tablosunu tercih ettiğiniz küçük veri kümeleri için şunları kullanın executemany():

cursor.execute("CREATE TABLE #Staging (ID INT, Name NVARCHAR(50))")
cursor.executemany(
    "INSERT INTO #Staging (ID, Name) VALUES (?, ?)",
    rows,
)