Wykonaj zapytania za pomocą mssql-python

Sterownik mssql-python zapewnia metody kursora do wykonywania zapytań SQL, parametryzowanych zapytań, operacji wsadowych oraz przygotowanych instrukcji.

Podstawowe wykonywanie zapytań

Użyj metody kursora execute() do uruchamiania instrukcji SQL:

import mssql_python

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

cursor.execute("SELECT Name, ListPrice FROM Production.Product WHERE Color = 'Black'")
rows = cursor.fetchall()

for row in rows:
    print(row.Name, row.ListPrice)

cursor.close()
conn.close()

Zapytania sparametryzowane

Zawsze używaj zapytań sparametryzowanych, aby zapobiec wstrzyknięciu kodu SQL. Domyślny styl parametrów sterownika to pyformat (nazwane symbole zastępcze), ale obsługuje także qmark (pozycyjne symbole zastępcze). Użyj qmark dla sekwencji ucieczki ODBC {CALL}.

Styl Pyformat (domyślny)

Używaj nazwanych symboli zastępczych w składni %(name)s i przekaż słownik:

cursor.execute(
    "SELECT Name, ListPrice FROM Production.Product WHERE Color = %(color)s AND ListPrice > %(price)s",
    {"color": "Black", "price": 10.00}
)

Styl Qmark

Używaj pozycyjnych symboli zastępczych z ? i przekaż krotkę lub listę:

cursor.execute(
    "SELECT Name, ListPrice FROM Production.Product WHERE ProductSubcategoryID = ? AND ListPrice > ?",
    (1, 10.00)
)

Sterownik automatycznie wykrywa styl parametrów na podstawie zapytań SQL i typów parametrów.

INSERT, UPDATE, DELETE operacje

Dla instrukcji modyfikacji danych używaj parametryzowanych zapytań i zatwierdzaj transakcję:

cursor.execute("CREATE TABLE #ExecDemo (Name NVARCHAR(50), CategoryID INT, Price DECIMAL(10,2))")
cursor.execute(
    "INSERT INTO #ExecDemo (Name, CategoryID, Price) VALUES (%(name)s, %(category)s, %(price)s)",
    {"name": "New Product", "category": 1, "price": 19.99}
)
conn.commit()

print(f"Rows affected: {cursor.rowcount}")

Wykonanie wsadowe za pomocą executemany()

Użyj executemany(), aby wydajnie wstawiać wiele wierszy. Sterownik stosuje kolumnowe wiązanie parametrów, aby uzyskać wysoką wydajność:

products = [
    {"name": "Product A", "category": 1, "price": 10.00},
    {"name": "Product B", "category": 1, "price": 15.00},
    {"name": "Product C", "category": 2, "price": 20.00},
]

cursor.execute("CREATE TABLE #BatchDemo (Name NVARCHAR(50), CategoryID INT, Price DECIMAL(10,2))")
cursor.executemany(
    "INSERT INTO #BatchDemo (Name, CategoryID, Price) VALUES (%(name)s, %(category)s, %(price)s)",
    products
)
conn.commit()

print(f"Rows inserted: {cursor.rowcount}")

W stylu „qmark”:

products = [
    ("Product A", 1, 10.00),
    ("Product B", 1, 15.00),
    ("Product C", 2, 20.00),
]

cursor.execute("CREATE TABLE #QmarkDemo (Name NVARCHAR(50), CategoryID INT, Price DECIMAL(10,2))")
cursor.executemany(
    "INSERT INTO #QmarkDemo (Name, CategoryID, Price) VALUES (?, ?, ?)",
    products
)
conn.commit()

Wsadowe wykonywanie wielu instrukcji

Użyj batch_execute() na połączeniu do wykonania wielu różnych instrukcji w jednym wywołaniu:

results, cursor = conn.batch_execute(
    [
        "CREATE TABLE #BatchExec (Name NVARCHAR(50), CategoryID INT)",
        "INSERT INTO #BatchExec (Name, CategoryID) VALUES (%(name)s, %(cat)s)",
        "SELECT COUNT(*) FROM #BatchExec"
    ],
    [
        None,                                 # No params for CREATE
        {"name": "New Item", "cat": 1},       # Params for INSERT
        None                                  # No params for SELECT
    ]
)

print(f"CREATE result: {results[0]}")
print(f"INSERT affected: {results[1]} rows")
print(f"Row count: {results[2][0][0]}")

Zapytania przygotowywane

Sterownik domyślnie przygotowuje zapytania (use_prepare=True). Gdy wykonasz ten sam ciąg SQL wielokrotnie na tym samym kursorze, sterownik automatycznie ponownie wykorzystuje przygotowane zdanie przy kolejnych wywołaniach:

# First execution prepares the statement
cursor.execute(
    "SELECT Name, ListPrice FROM Production.Product WHERE ProductSubcategoryID = %(subcategory_id)s",
    {"subcategory_id": 1},
)
rows1 = cursor.fetchall()

# Same SQL string on same cursor → driver reuses the prepared plan
cursor.execute(
    "SELECT Name, ListPrice FROM Production.Product WHERE ProductSubcategoryID = %(subcategory_id)s",
    {"subcategory_id": 2},
)
rows2 = cursor.fetchall()

Aby pominąć przygotowanie i użyć bezpośredniego wykonania:

cursor.execute(
    "SELECT Name, ListPrice FROM Production.Product WHERE ProductSubcategoryID = 1",
    use_prepare=False  # Uses SQLExecDirectW instead of SQLPrepareW
)

Wykonywanie na poziomie połączenia

Dla prostych, jednorazowych zapytań użyj execute() bezpośrednio na połączeniu:

# Creates cursor, executes, and returns cursor
cursor = conn.execute("SELECT TOP 10 Name, ListPrice FROM Production.Product")
rows = cursor.fetchall()
cursor.close()

Procedury przechowywane

Wywołuj procedury przechowywane przy użyciu EXECUTE lub składni escape ODBC {CALL}. Aby uzyskać informacje o parametrach wyjściowych, wielu zestawach wyników i wzorcach transakcyjnych, zobacz Procedury przechowywane.

cursor.execute(
    "EXECUTE dbo.uspGetManagerEmployees @BusinessEntityID = %(business_entity_id)s",
    {"business_entity_id": 16}
)
rows = cursor.fetchall()

Ustaw rozmiary wejść

Użyj setinputsizes(), aby jawnie deklarować typy parametrów, co może poprawić wydajność w przypadku operacji wsadowych:

cursor.setinputsizes([
    (mssql_python.SQL_WVARCHAR, 50, 0),   # NVARCHAR(50)
    (mssql_python.SQL_INTEGER, 0, 0),     # INT
])

cursor.executemany(
    "SELECT ProductID, Name FROM Production.Product WHERE Name LIKE ? AND ProductSubcategoryID = ?",
    [("Road%", 2), ("Mountain%", 1)]
)

Note

Nie wszystkie stałe typów SQL działają z setinputsizes(). SQL_WVARCHAR i SQL_INTEGER są niezawodne. Dla wartości dziesiętnych użyj automatycznego wnioskowania typów sterownika zamiast SQL_DECIMAL, które ma znany problem (GitHub #503).

Obsługa błędów

Umieść operacje na bazie danych w blokach try-except:

try:
    cursor.execute("CREATE TABLE #ErrDemo (Name NVARCHAR(50) NOT NULL)")
    cursor.execute("INSERT INTO #ErrDemo (Name) VALUES (%(name)s)", {"name": None})
    conn.commit()
except mssql_python.IntegrityError as e:
    print(f"Constraint violation: {e}")
    conn.rollback()
except mssql_python.ProgrammingError as e:
    print(f"SQL error: {e}")
    conn.rollback()

Najlepsze rozwiązania

  1. Zawsze używaj parametryzowanych zapytań , aby zapobiec wstrzykiwaniu SQL.
  2. Używaj bulk copy do zbiorczego wstawiania danych zamiast wielu wywołań execute().
  3. Jawnie zatwierdzaj transakcje, gdy wyłączysz tryb autocommit.
  4. Zamykaj kursory i połączenia , gdy to robisz, aby uwolnić zasoby.
  5. Używaj menedżerów kontekstu do automatycznego czyszczenia zasobów:
with mssql_python.connect(connection_string) as conn:
    with conn.cursor() as cursor:
        cursor.execute("SELECT TOP 5 Name, ListPrice FROM Production.Product")
        rows = cursor.fetchall()
# Connection and cursor automatically closed