Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
El controlador mssql-python es el controlador Python de primera mano de Microsoft para Microsoft SQL. Si prefieres una opción de controlador mantenida por Microsoft, ofrece:
- No requiere controladores ODBC externos.
- Agrupación de conexiones integrada.
- Soporte moderno para Python 3.10+.
- Autenticación nativa de Microsoft Entra.
Diferencias clave
| Feature | pyodbc | mssql-python |
|---|---|---|
| Estilo de parámetro |
qmark (?) |
qmark (?) y pyformat (%(name)s) |
| Se requiere controlador ODBC | Sí | No |
| Agrupación de conexiones | Externo | Integrado |
| Versión mínima de Python | 3.6 | 3.10 |
callproc() |
Soportado | No implementado |
| Confirmación automática predeterminada | Off | Off |
Pasos básicos de migración
Los siguientes pasos cubren los cambios clave para migrar una aplicación pyodbc a mssql-python.
1. Actualizar importaciones
Sustituye la pyodbc importación por mssql_python:
Antes (pyodbc):
import pyodbc
Después de (mssql-python):
import mssql_python
2. Actualizar cadenas de conexión
Elimina la DRIVER= palabra clave y actualiza el método de autenticación:
Antes (pyodbc, requiere un controlador ODBC):
conn = pyodbc.connect(
"DRIVER={ODBC Driver 18 for SQL Server};"
"SERVER=localhost;"
"DATABASE=AdventureWorks2022;"
"Trusted_Connection=yes;"
)
Después (mssql-python, sin necesidad de controlador, usando la autenticación de Microsoft Entra):
conn = mssql_python.connect(
"Server=<server>.database.windows.net;"
"Database=<database>;"
"Authentication=ActiveDirectoryDefault;"
"Encrypt=yes;"
)
3. Mantén tus consultas tal cual
El controlador mssql-python admite tanto el estilo de parámetros ? (qmark) como el estilo de parámetros %(name)s (pyformat). Tus consultas existentes ? funcionan sin cambios:
Antes (pyodbc):
cursor.execute("SELECT * FROM Production.Product WHERE ProductID = ? AND Color = ?", (1, "Red"))
Después (mssql-python, misma consulta):
cursor.execute("SELECT * FROM Production.Product WHERE ProductID = ? AND Color = ?", (1, "Red"))
4. Mantén executemany tal cual
Las llamadas existentes executemany con tuplas y marcadores ? funcionan sin cambios:
Antes (pyodbc):
cursor.execute("CREATE TABLE #MigrateDemo (ID INT, Name NVARCHAR(50))")
data = [(1, "Alice"), (2, "Bob"), (3, "Carol")]
cursor.executemany("INSERT INTO #MigrateDemo (ID, Name) VALUES (?, ?)", data)
Después (mssql-python, mismo código):
cursor.execute("DROP TABLE IF EXISTS #MigrateDemo")
cursor.execute("CREATE TABLE #MigrateDemo (ID INT, Name NVARCHAR(50))")
data = [(1, "Alice"), (2, "Bob"), (3, "Carol")]
cursor.executemany("INSERT INTO #MigrateDemo (ID, Name) VALUES (?, ?)", data)
Migración de procedimientos almacenados
El controlador mssql-python no implementa callproc(). Las siguientes secciones muestran cómo usarlo EXECUTE en su lugar.
Usar EXECUTE para procedimientos almacenados
El controlador pyodbc soporta callproc(), pero el controlador mssql-python no. Use EXECUTE en su lugar:
Antes (pyodbc):
cursor.callproc("dbo.uspGetEmployeeManagers", (5,))
results = cursor.fetchall()
Después de (mssql-python):
cursor.execute(
"EXECUTE dbo.uspGetEmployeeManagers @BusinessEntityID = %(id)s",
{"id": 5}
)
results = cursor.fetchall()
print(f"Got {len(results)} rows")
Parámetros de salida
Utiliza variables T-SQL para capturar valores de salida en lugar de depender de callproc() parámetros de salida:
Antes (pyodbc, usando callproc):
params = (category_id, pyodbc.SQL_INTEGER)
cursor.callproc("dbo.GetProductCount", params)
count = params[1].value
Después (mssql-python, usando variables T-SQL):
cursor.execute(
"""
DECLARE @count INT;
SELECT @count = COUNT(*) FROM Production.Product
WHERE ProductSubcategoryID = %(cat_id)s;
SELECT @count AS ProductCount;
""",
{"cat_id": 1}
)
product_count = cursor.fetchval()
print(f"Product count: {product_count}")
Migraciones específicas de una función
Las siguientes secciones cubren características específicas de pyodbc y sus equivalentes mssql-python.
Cadenas de conexión
| Palabra clave pyodbc | Palabra clave mssql-python | Notas |
|---|---|---|
DRIVER={...} |
No es necesario | El controlador ODBC viene incluido internamente. |
SERVER= |
Server= |
Ningún cambio de comportamiento. |
DATABASE= |
Database= |
Ningún cambio de comportamiento. |
Trusted_Connection= |
Trusted_Connection= |
Ningún cambio de comportamiento. |
UID= / PWD= |
UID= / PWD= |
Ningún cambio de comportamiento. |
Authentication= |
Authentication= |
Acepta los mismos valores. |
Compromiso automático
El comportamiento de autocommit es idéntico en ambos controladores:
pyodbc:
conn.autocommit = True
pyodbc.connect(connection_string, autocommit=True)
MSSQL-Python:
conn.autocommit = True
Inserciones masivas
Para acelerar lotes INSERT grandes, los usuarios de pyodbc configuran fast_executemany = True. El controlador mssql-python ya optimiza executemany para lotes parametrizados, por lo que insertos moderados no necesitan una bandera especial. Para cargas de datos grandes, prefiere bulkcopy(), que transmite las filas mediante el protocolo de copia en bloque y es mucho más rápido que ejecutar instrucciones INSERT individuales. Para ver el flujo de trabajo completo, véase Usar copia masiva.
pyodbc:
cursor.fast_executemany = True
cursor.executemany(query, data)
Después de (mssql-python), lotes moderados con executemany:
cursor.execute("DROP TABLE IF EXISTS #BulkTarget")
cursor.execute("CREATE TABLE #BulkTarget (ID INT, Name NVARCHAR(50))")
data = [(i, f"Item {i}") for i in range(100)]
cursor.executemany("INSERT INTO #BulkTarget (ID, Name) VALUES (?, ?)", data)
conn.commit()
Tras (mssql-python), cargas de gran volumen con bulkcopy (opción preferida):
cursor.execute("IF OBJECT_ID('##BulkTarget') IS NOT NULL DROP TABLE ##BulkTarget")
cursor.execute("CREATE TABLE ##BulkTarget (ID INT, Name NVARCHAR(50))")
conn.commit() # Commit DDL before bulkcopy
data = [(i, f"Item {i}") for i in range(100)]
result = cursor.bulkcopy("##BulkTarget", data)
print(f"Bulk copied {result['rows_copied']} rows")
cursor.execute("DROP TABLE ##BulkTarget")
conn.commit()
Fábrica de remos
El controlador mssql-python devuelve Row objetos que admiten acceso a atributos por defecto, sin requerir una fábrica de filas personalizada:
PYODBC (fábrica de filas personalizadas):
def namedtuple_row_factory(cursor):
from collections import namedtuple
columns = [col[0] for col in cursor.description]
Row = namedtuple("Row", columns)
return Row
mssql-python (acceso a atributos por defecto):
cursor.execute("SELECT Name, ListPrice FROM Production.Product")
row = cursor.fetchone()
print(row.Name) # Attribute access works directly
print(row[0]) # Index access also works
Gestión de errores
El controlador mssql-python utiliza la misma jerarquía de excepciones que pyodbc, por lo que la mayoría de los gestores de excepciones solo requieren un cambio de nombre de módulo.
Jerarquía de excepciones
Los nombres de las clases de excepción se corresponden directamente entre los controladores:
pyodbc:
try:
cursor.execute(query)
except pyodbc.Error as e:
pass
except pyodbc.DatabaseError as e:
pass
except pyodbc.OperationalError as e:
pass
MSSQL-Python:
try:
cursor.execute("SELECT TOP 1 * FROM Production.Product")
print(cursor.fetchone())
except mssql_python.Error as e:
pass
except mssql_python.DatabaseError as e:
pass
except mssql_python.OperationalError as e:
pass
Detalles del error
Ambos controladores exponen detalles de error mediante argumentos de excepción:
pyodbc:
try:
cursor.execute(query)
except pyodbc.Error as e:
sqlstate = e.args[0]
message = e.args[1]
MSSQL-Python:
try:
cursor.execute("SELECT TOP 1 * FROM NonExistentTable_XYZ")
except mssql_python.Error as e:
# Error message contains SQLSTATE and details
print(str(e))
Agrupación de conexiones
El controlador mssql-python incluye agrupación de conexiones por defecto, por lo que ya no se requieren bibliotecas externas de pooling.
Eliminar el agrupamiento externo
Si usaste pooling externo con pyodbc, el controlador mssql-python lo tiene integrado:
Antes (pool externo de pyodbc):
from dbutils.pooled_db import PooledDB
pool = PooledDB(pyodbc, 5, driver="{ODBC Driver 18 for SQL Server}",
server="your_server", database="your_database",
uid="your_username", pwd="your_password")
conn = pool.connection()
Después (con agrupación de conexiones incorporada de mssql-python):
conn = mssql_python.connect(connection_string)
conn.close()
Configurar grupo
Anula el tamaño y el tiempo de espera por defecto del pool con mssql_python.pooling():
import mssql_python
mssql_python.pooling()
Ejemplo de migración completa
Lo siguiente muestra la misma función escrita con pyodbc y luego reescrita con mssql-python.
Antes (pyodbc)
Esta versión utiliza la cadena de conexión pyodbc con una DRIVER palabra clave:
import pyodbc
from datetime import date
def get_orders(customer_id: int, start_date: date):
conn = pyodbc.connect(
"DRIVER={ODBC Driver 18 for SQL Server};"
"SERVER=localhost;"
"DATABASE=AdventureWorks2022;"
"Trusted_Connection=yes;"
)
cursor = conn.cursor()
cursor.execute("""
SELECT SalesOrderID, OrderDate, TotalDue
FROM Sales.SalesOrderHeader
WHERE CustomerID = ? AND OrderDate >= ?
ORDER BY OrderDate DESC
""", (customer_id, start_date))
orders = []
for row in cursor:
orders.append({
"id": row.SalesOrderID,
"date": row.OrderDate,
"total": row.TotalDue
})
cursor.close()
conn.close()
return orders
Después (mssql-python)
Esta versión elimina la DRIVER palabra clave. Todas las consultas, parámetros y patrones de acceso a filas permanecen idénticos:
import mssql_python
from datetime import date
def get_orders(customer_id: int, start_date: date):
conn = mssql_python.connect(
"Server=localhost;"
"Database=AdventureWorks2022;"
"Trusted_Connection=yes;"
)
cursor = conn.cursor()
cursor.execute("""
SELECT SalesOrderID, OrderDate, TotalDue
FROM Sales.SalesOrderHeader
WHERE CustomerID = ? AND OrderDate >= ?
ORDER BY OrderDate DESC
""", (customer_id, start_date))
orders = []
for row in cursor:
orders.append({
"id": row.SalesOrderID,
"date": row.OrderDate,
"total": row.TotalDue
})
cursor.close()
conn.close()
return orders
Los únicos cambios son la instrucción import y la cadena de conexión (no se necesita la palabra clave DRIVER). Cada consulta, parámetro, patrón de obtención y acceso a filas permanece idéntico.
Prueba de la migración
Antes de completar la migración, ejecuta las mismas consultas con ambos controladores y compara los resultados para confirmar el comportamiento equivalente.
Verificar comportamiento equivalente
Utiliza una función de comparación que ejecute la misma consulta contra ambos controladores y afirme que los resultados coinciden:
import pyodbc
import mssql_python
def compare_results(pyodbc_conn_str: str, mssql_conn_str: str, query: str):
"""Compare results from both drivers."""
# pyodbc query
pyodbc_conn = pyodbc.connect(pyodbc_conn_str)
pyodbc_cursor = pyodbc_conn.cursor()
pyodbc_cursor.execute(query)
pyodbc_results = pyodbc_cursor.fetchall()
pyodbc_conn.close()
# mssql-python query
mssql_conn = mssql_python.connect(mssql_conn_str)
mssql_cursor = mssql_conn.cursor()
mssql_cursor.execute(query)
mssql_results = mssql_cursor.fetchall()
mssql_conn.close()
# Compare
assert len(pyodbc_results) == len(mssql_results)
for p_row, m_row in zip(pyodbc_results, mssql_results):
assert tuple(p_row) == tuple(m_row)
print(f"Results match: {len(pyodbc_results)} rows")
Checklist
- [ ] Actualizar importaciones desde
pyodbcamssql_python. - [ ] Quite
DRIVER=de las cadenas de conexión. - [ ] Mantén las consultas de parámetros existentes
?(funcionan as-is). - [ ] Usa
EXECUTEsentencias para llamadas a procedimientos almacenados. - [ ] Eliminar la configuración de la agrupación externa de conexiones.
- [ ] Actualizar los nombres de las clases de manejo de excepciones.
- [ ] Prueba todas las consultas y procedimientos almacenados.
- [ ] Verifica el manejo de los tipos de datos (especialmente decimales y fechas).
- [ ] Eliminar el controlador ODBC de los requisitos de despliegue.