Felsök problem med databasfrågor, data och operationer med mssql-python

Använd denna artikel för att diagnostisera problem med frågeexekvering, datatyp, prestanda, transaktioner och bulkkopiering av drivrutinen mssql-python .

Problem med frågeexekvering

Tabell eller objekt hittades ej

Symtom:

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

Möjliga orsaker och lösningar:

  • Felaktig databaskontext

    cursor.execute("SELECT DB_NAME()")
    print(cursor.fetchone()[0])
    
  • Schema ej specificerat

    cursor.execute("SELECT * FROM dbo.TableName")
    
  • Tabellen finns inte

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

Syntaxfel

Symtom:

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

Lösningar:

  1. Testa SQL-satsen i SQL Server Management Studio (SSMS) för att verifiera syntaxen.

  2. Använd en parameteriserad fråga istället för stränginterpolation:

    # 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},
    )
    

Parameterfel

Symtom:

ProgrammingError: [07001] Wrong number of parameters

Lösningar:

  1. Räkna platshållarna och parametrarna. Räkningarna måste stämma.

  2. Välj rätt parameterstil:

    # 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())
    

Felaktiga radräkningar i batcher med flera resultat

Om rowcount upprepar antalet för den första satsen efter nextset(), uppgradera drivrutinen. Varje lyckat nextset() uppdaterar räkningen för det nya resultatet. Ett resultat som producerar rader startar vid -1 före hämtning. Kontrollera cursor.description innan du hämtar eftersom datamodifieringsresultat innehåller räkningar istället för rader.

Låt SET NOCOUNT vara avstängt när du behöver resultat från berörda rader. För ett komplett exempel, se Spåra varje resultat i en batch.

Datatypproblem

Fel vid konvertering av datum och tid

Symtom:

DataError: [22007] Invalid datetime format

Solution:

Använd Python-objekt datetime istället för strängar.

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())

Tidsstämpelkonstruktören kräver ett mikrosekundsargument

Om Timestamp(year, month, day, hour, minute, second) genererar TypeError för ett saknat microsecond-argument, uppgradera mssql-python. Den nuvarande drivrutinen accepterar DB-API-konstruktorn med sex argument och har microsecond som standardvärde 0. För mer information, se DB-API timestamp constructor.

Decimalprecisionsproblem

Symtom:

Siffror visas avkortade eller avrundade felaktigt.

Solution:

Användning decimal.Decimal för exakta numeriska värden:

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

Symtom:

Specialtecken verkar osammanhängande eller orsakar fel.

Lösningar:

  1. Använd nvarchar-kolumner för Unicode-data i din databas.

  2. Passa strängarna direkt. Drivrutinen hanterar kodningen:

    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())
    

Prestandaproblem

Långsam frågeexekvering

Möjliga orsaker och lösningar:

  • Saknade index: Kontrollera frågeexekveringsplanen i SSMS.

  • Stora resultatmängder: Använd fetchmany() istället för fetchall():

    cursor.arraysize = 1000
    while True:
        rows = cursor.fetchmany()
        if not rows:
            break
        process_rows(rows)
    
  • Anslutningspoolning inaktiverad: Aktivera poolning:

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

Minnesproblem med stora resultat

Symtom:

Python-processen får slut på minne.

Lösningar:

  1. Strömma resultat istället för att ladda alla rader i minnet.

    cursor.execute("SELECT * FROM LargeTable")
    for row in cursor:
        process_row(row)
    
  2. Använd serverside-paginering.

    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
    

Transaktionsproblem

Temporär tabellscoping med autocommit

Sessionstemporära tabeller (#tablename) du skapar i en transaktion försvinner när transaktionen rullas tillbaka. Detta beteende orsakar ofta förvirring när autocommit är avstängt, vilket är standard:

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

Bekräfta omedelbart efter att du har skapat en temporär tabell, eller använd autocommit-läge:

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

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

DDL-satser som kräver autocommit mode, såsom CREATE DATABASE, misslyckas inom en öppen transaktion. Ställ in autocommit innan du kör dem:

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

Transaktion som inte är genomförd

Symtom:

Dataändringar kvarstår inte efter att du stängt anslutningen.

Solution:

Med autocommit=False, vilket är standard, anropa commit():

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

Alternativt, använd autocommit-läge:

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

Dödlägesfel

Symtom:

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

Solution:

Ompröva-logiken hanterar det omedelbara felet, men återkommande deadlocks indikerar ett designproblem. Samla in dödlägesgrafen och analysera instruktionerna och låstyperna. Vanliga lösningar inkluderar dessa förändringar:

  • Ordna om operationerna så att konkurrerande transaktioner inhämtar lås i samma ordning.
  • Minska transaktionsomfattningen.
  • Lägg till lämpliga index för att minska låsets varaktighet.

För en fullständig genomgång av deadlock-analys, se Deadlocks-guiden. Om du använder Azure SQL Database, se Analysera och förhindra deadlocks.

Bulklastproblem

Masskopiering misslyckas efter en paketuppgradering

Om vanliga frågor fungerar men bulkcopy() eller bulkcopy_arrow() misslyckas med att importera eller ladda den inbyggda bulkkopieringsmodulen, uppgradera drivrutinen med beroenden:

python -m pip install --upgrade mssql-python
python -m pip check

Drivrutinen kräver en matchande mssql-python-rs runtime-miljö. En partiell uppgradering eller installation med --no-deps kan lämna inkompatibla paket installerade. Om du använder ett privat paketindex, spegla båda följeslagarpaketen och bevara driverns deklarerade beroendelåsningar. För de aktuella pinningarna, se Beroendekrav.

Destinationsnamn som inte stöds

bulkcopy() och bulkcopy_arrow() accepterar högst tre identifieringsdelar. Ett fyrdelat namn som server.database.schema.table orsakar RuntimeError under metadatauppslagning innan några rader skickas. Anslut dig direkt till destinationsservern och använd ett endelat, tvådelat eller tredelat tabellnamn. För escapning av hakparenteser och fel för felaktigt utformade namn, se Måltabellsnamn.

Unicode eller bråkdelar av sekunder förloras vid masskopiering

Uppgradera drivrutinen om bulkkopiering förvanskar UTF-8-texten eller trunkerar sekundbråkdelar vid inläsning av kompatibla datetime2-värden. Den nuvarande masskopieringsruntimen hanterar UTF-8-text och bevarar datetime2-precisionen som finns tillgänglig i käll- och destinationstyperna.

Använd nvarchar eller en UTF-8-sortering på varchar för Unicode-text. Python-värden datetime och drivrutinens Arrow-hämtametoder är begränsade till mikrosekunder. För 100-nanosekunders värden som redan finns i en Arrow-nanosekundstidsstämpelkolumn, ladda in datetime2(7) utan att först konvertera till Pythondatetime. För mer information, se Unicode och tidsmässig precision samt Arrow tidsprecision.

Begränsningsbrott under bulkkopiering

Symtom:

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

Orsak:

Data i din batch bryter mot tabellbegränsningar såsom primärnyckel-, unik-, kontroll- eller främmande nyckelbegränsningar.

Solution:

Validera data innan du laddar den. För stora datamängder, ladda in datan i en stagingtabell och slå sedan ihop den med målet:

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()

För upsert-mönster med stagingtabeller, se Data loading and movement patterns.

Fel vid kolumnmappning

Symtom:

RuntimeError: Bulk copy failure - column count mismatch

Orsak:

Antalet kolumner i din data stämmer inte överens med antalet kolumner i måltabellen, eller så är kolumnerna i fel ordning.

Solution:

Se till att dina data matchar tabellschemat i ordning och antal:

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)

Typkonflikter vid bulkkopiering

Symtom:

Data läses in, men värdena är trunkerade, avrundade eller felaktiga.

Orsak:

Python-värden mappas inte rent till målkolumntyperna. Vanliga exempel inkluderar float värden som laddas i decimalkolumner , vilka kan tappa precision, och överdimensionerade strängar som laddas i kolumner med fast längd.

Solution:

Använd Python-typer som matchar ditt schema:

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)

Bindningsfel av NumPy-typ

Symtom:

Parametrar misslyckas tyst eller ger datatypfel när du använder NumPy heltals- eller flyttal-typer.

Orsak:

NumPy-typer som numpy.int64 och numpy.int32 passerar isinstance(x, int) inte i NumPy 2.x. Förarens typinferens känner inte igen dem, vilket orsakar oväntat beteende.

Solution:

Konvertera NumPy-värden till inbyggda Python-typer innan du binder dem:

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

För större datauppsättningar, använd integreringsvägarna för Arrow eller pandas. Dessa vägar hanterar typkonvertering internt.

Bulkkopiering med temporära tabeller

Symtom:

cursor.bulkcopy("#TempTable", data) höjer RuntimeError: Invalid object name '#TempTable'.

Orsak:

bulkcopy() Kan inte lösa sessionstemporära tabeller (#tablename) på grund av begränsningar i metadatauppslag. Globala tillfälliga tabeller (##tablename) och permanenta tabeller fungerar.

Solution:

Använd en global temptabell eller en vanlig staging-tabell:

# 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)

För små datauppsättningar där du föredrar en temporär sessionstabell, använd executemany():

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