Jalankan kueri dengan mssql-python

Driver mssql-python menyediakan metode kursor untuk eksekusi kueri SQL, kueri berparameter, operasi batch, dan pernyataan yang disiapkan.

Eksekusi kueri dasar

Gunakan metode kursor execute() untuk menjalankan pernyataan 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()

Kueri dengan parameter

Selalu gunakan kueri berparameter untuk mencegah injeksi SQL. Paramstyle bawaan driver adalah pyformat (placeholder bernama), namun juga mendukung qmark (placeholder berdasarkan posisi). Gunakan qmark untuk urutan escape ODBC {CALL} .

Format Pyformat (bawaan)

Gunakan placeholder bernama dengan sintaks %(name)s dan berikan kamus:

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

Gaya Qmark

Gunakan placeholder posisional dengan ? dan teruskan tuple atau daftar:

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

Driver secara otomatis mendeteksi gaya parameter berdasarkan kueri SQL dan jenis parameter Anda.

INSERT, UPDATE, DELETE operasi

Untuk pernyataan modifikasi data, gunakan kueri berparameter dan terapkan transaksi:

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

Eksekusi secara batch dengan executemany()

Gunakan executemany() untuk menyisipkan beberapa baris secara efisien. Driver ini menggunakan pengikatan parameter berdasarkan kolom untuk kinerja tinggi:

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

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

Eksekusi batch beberapa pernyataan

Gunakan batch_execute() pada koneksi untuk mengeksekusi beberapa pernyataan berbeda dalam satu panggilan:

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

Pernyataan yang disiapkan

Driver menyiapkan kueri secara default (use_prepare=True). Saat Anda menjalankan string SQL yang sama beberapa kali pada kursor yang sama, driver secara otomatis menggunakan kembali pernyataan yang disiapkan pada panggilan berikutnya:

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

Untuk melewati persiapan dan menggunakan eksekusi langsung sebagai gantinya:

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

Eksekusi pada tingkat koneksi

Untuk kueri satu kali sederhana, gunakan execute() langsung pada koneksi:

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

Prosedur yang disimpan

Panggil prosedur tersimpan dengan menggunakan EXECUTE atau sintaks escape ODBC {CALL} . Untuk informasi tentang parameter output, beberapa set hasil, dan pola transaksi, lihat Prosedur tersimpan.

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

Atur ukuran input

Gunakan setinputsizes() untuk mendeklarasikan jenis parameter secara eksplisit, yang dapat meningkatkan performa untuk operasi batch:

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

Tidak semua konstanta jenis SQL berfungsi dengan setinputsizes(). SQL_WVARCHAR dan SQL_INTEGER dapat diandalkan. Untuk nilai desimal, gunakan inferensi tipe otomatis pada driver alih-alih SQL_DECIMAL, yang diketahui memiliki masalah (GitHub #503).

Penanganan kesalahan

Bungkus operasi basis data dalam blok 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()

Praktik terbaik

  1. Selalu gunakan kueri berparameter untuk mencegah injeksi SQL.
  2. Gunakan penyalinan massal untuk penyisipan massal alih-alih beberapa pemanggilan execute().
  3. Terapkan transaksi secara eksplisit saat Anda menonaktifkan penerapan otomatis.
  4. Tutup kursor dan koneksi saat selesai untuk melepaskan sumber daya.
  5. Gunakan pengelola konteks untuk pembersihan sumber daya otomatis:
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