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 dependía de FreeTDS.
- Múltiples cursores concurrentes por conexión.
- Agrupación de conexiones integrada.
- Soporte moderno para Python 3.10+.
- Autenticación nativa de Microsoft Entra.
- Objetos de fila con acceso por atributos de forma predeterminada.
Diferencias clave
| Feature | pymssql | mssql-python |
|---|---|---|
| Estilo de parámetro |
format (%s, %d) |
qmark (?) y pyformat (%(name)s) |
| Biblioteca nativa | FreeTDS | DDBC (incluido en el paquete) |
| Agrupación de conexiones | Externo | Integrado |
| Cursores por conexión | 1 | Múltiple |
| Versión mínima de Python | 3.6 | 3.10 |
callproc() |
Soportado | No implementado |
as_dict cursor |
Extension | Objetos de fila (por defecto) |
| Copia masiva | conn.bulk_copy() |
cursor.bulkcopy() |
| Confirmación automática predeterminada | Off | Off |
Pasos básicos de migración
Los siguientes pasos repasan los cambios más comunes necesarios para migrar una aplicación pymssql a mssql-python.
1. Actualizar importaciones
Sustituye la pymssql importación por mssql_python:
Antes (pymssql):
import pymssql
Después de (mssql-python):
import mssql_python
2. Actualizar llamadas de conexión
PymsSQL utiliza argumentos posicionales. El controlador mssql-python utiliza una cadena de conexión o argumentos de palabras clave:
Antes (pymssql, argumentos posicionales):
conn = pymssql.connect("<server>", "<user>", "<password>", "<database>")
Antes (pymssql, argumentos por palabra clave):
conn = pymssql.connect(
host=r"<server>\<instance>",
user="<login>",
password="<password>",
database="<database>"
)
Después (mssql-python, cadena de conexión):
conn = mssql_python.connect(
"Server=<server>;"
"Database=<database>;"
"UID=<username>;"
"PWD=<password>;"
"Encrypt=yes;"
)
Después (mssql-python, recomendado por Microsoft Entra):
conn = mssql_python.connect(
"Server=<server>.database.windows.net;"
"Database=<database>;"
"Authentication=ActiveDirectoryDefault;"
"Encrypt=yes;"
)
3. Actualizar marcadores de parámetros
pymssql utiliza los marcadores de formato %s y %d. El controlador mssql-python utiliza ? (qmark) o %(name)s (pyformat):
Antes (pymssql):
cursor.execute("SELECT * FROM Person.Person WHERE BusinessEntityID = %d AND FirstName = %s", (user_id, name))
Después (mssql-python, estilo qmark):
user_id, name = 1, "Ken"
cursor.execute("SELECT * FROM Person.Person WHERE BusinessEntityID = ? AND FirstName = ?", (user_id, name))
print(cursor.fetchone())
cursor.execute(
"SELECT * FROM Person.Person WHERE BusinessEntityID = %(id)s AND FirstName = %(name)s",
{"id": user_id, "name": name}
)
print(cursor.fetchone())
4. Actualizar executemany
Actualizar los marcadores de posición de SQL de %s/%d a ? o %(name)s
Antes (pymssql):
cursor.execute("CREATE TABLE #Persons (ID INT, Name NVARCHAR(50), Department NVARCHAR(50))")
cursor.executemany(
"INSERT INTO #Persons VALUES (%d, %s, %s)",
[(1, "John", "Sales"), (2, "Jane", "Marketing")]
)
Después (mssql-python, estilo qmark):
cursor.execute("IF OBJECT_ID('#Persons') IS NOT NULL DROP TABLE #Persons")
cursor.execute("CREATE TABLE #Persons (ID INT, Name NVARCHAR(50), Department NVARCHAR(50))")
cursor.executemany(
"INSERT INTO #Persons VALUES (?, ?, ?)",
[(1, "John", "Sales"), (2, "Jane", "Marketing")]
)
cursor.execute("SELECT * FROM #Persons")
for row in cursor:
print(row)
5. Usar atributos de fila en lugar de as_dict cursores
pymssql requiere as_dict=True para acceder a las columnas por nombre. El controlador mssql-python devuelve Row objetos que admiten tanto el acceso a atributos como a índices por defecto:
Antes (pymssql):
cursor = conn.cursor(as_dict=True)
cursor.execute("SELECT BusinessEntityID, FirstName FROM Person.Person WHERE FirstName = %s", ("John",))
for row in cursor:
print("ID=%d, Name=%s" % (row["BusinessEntityID"], row["FirstName"]))
Después (mssql-python, acceso a atributos por defecto):
cursor = conn.cursor()
cursor.execute("SELECT BusinessEntityID, FirstName FROM Person.Person WHERE FirstName = ?", ("John",))
for row in cursor:
print(f"ID={row.BusinessEntityID}, Name={row.FirstName}")
# Index access also works: row[0], row[1]
Migración de procedimientos almacenados
El controlador mssql-python no implementa callproc(). Utiliza EXECUTE sentencias en su lugar.
Usar EXECUTE para procedimientos almacenados
PymsSQL soporta callproc(), pero el controlador mssql-python no. Use EXECUTE en su lugar:
Antes (pymssql):
cursor.callproc("uspGetEmployeeManagers", (5,))
for row in cursor:
print(row)
Después de (mssql-python):
cursor.execute("EXECUTE dbo.uspGetEmployeeManagers @BusinessEntityID = ?", (5,))
for row in cursor:
print(row)
Parámetros de salida
Utiliza variables T-SQL para capturar valores de salida en lugar de depender de callproc() parámetros de salida:
Antes (pymssql):
cursor.callproc("GetProductCount", (category_id,))
count = cursor.fetchval()
Después (mssql-python, variables de T-SQL):
cursor.execute("""
DECLARE @count INT;
SELECT @count = COUNT(*) FROM Production.Product
WHERE ProductSubcategoryID = ?;
SELECT @count AS ProductCount;
""", (1,))
product_count = cursor.fetchval()
print(f"Product count: {product_count}")
Migración masiva de copias
pymssql llama a bulk_copy() en la conexión. El controlador mssql-python llama a bulkcopy() en el cursor con más opciones:
Antes (pymssql):
conn.bulk_copy("##BulkDemo", [(1, 2)] * 1000)
conn.commit()
Después de (mssql-python):
cursor = conn.cursor()
cursor.execute("CREATE TABLE ##BulkDemo (Col1 INT, Col2 INT)")
conn.commit()
result = cursor.bulkcopy("##BulkDemo", [(1, 2)] * 1000)
print(f"Copied {result['rows_copied']} rows")
conn.commit()
cursor.execute("DROP TABLE ##BulkDemo")
conn.commit()
El método mssql-python bulkcopy() soporta batch_size, timeout, column_mappings, keep_identity, check_constraintstable_lock, keep_nulls, fire_triggers, y use_internal_transaction. Consulta la copia en bloque para obtener más información.
Varios cursores
PymsSQL solo permite un cursor activo por conexión. El controlador mssql-python soporta múltiples cursores concurrentes:
Antes (pymssql):
c1 = conn.cursor()
c1.execute("SELECT TOP 5 * FROM Person.Person")
c2 = conn.cursor()
c2.execute("SELECT TOP 5 * FROM Sales.SalesOrderHeader")
c1.fetchall()
Después de (mssql-python):
c1 = conn.cursor()
c1.execute("SELECT TOP 5 BusinessEntityID, FirstName FROM Person.Person")
persons = c1.fetchall()
c2 = conn.cursor()
c2.execute("SELECT TOP 5 SalesOrderID FROM Sales.SalesOrderHeader")
orders = c2.fetchall()
print(f"Persons: {len(persons)}, Orders: {len(orders)}")
Agrupación de conexiones
PymsSQL no tiene pooling incorporado. El controlador mssql-python lo incluye automáticamente:
Antes (pymssql, se requiere un pool externo):
from dbutils.pooled_db import PooledDB
pool = PooledDB(pymssql, host="server", user="user", password="pwd", database="db")
conn = pool.connection()
Después de (mssql-python, el pooling es automático):
conn = mssql_python.connect(connection_string)
conn.close()
Gestión de errores
El controlador mssql-python utiliza la misma jerarquía de excepciones que pymssql, por lo que la mayoría de los gestores de excepciones solo requieren un cambio de nombre de módulo:
Antes (pymssql):
try:
cursor.execute(query)
except pymssql.OperationalError as e:
print(f"Operation failed: {e}")
except pymssql.InterfaceError as e:
print(f"Interface error: {e}")
Después de (mssql-python):
try:
cursor = conn.cursor()
cursor.execute("SELECT TOP 1 * FROM Production.Product")
row = cursor.fetchone()
print(row)
except mssql_python.OperationalError as e:
print(f"Operation failed: {e}")
except mssql_python.InterfaceError as e:
print(f"Interface error: {e}")
Ejemplo de migración completa
Lo siguiente muestra la misma función escrita con pymssql y luego reescrita con mssql-python.
Antes (pymssql)
Esta versión utiliza argumentos de conexión posicional, as_dict=True, y %d marcadores de parámetros:
import pymssql
def get_orders(customer_id: int):
conn = pymssql.connect("<server>", "<user>", "<password>", "<database>")
cursor = conn.cursor(as_dict=True)
cursor.execute("""
SELECT TOP 10 SalesOrderID, OrderDate, TotalDue
FROM Sales.SalesOrderHeader
WHERE CustomerID = %d
ORDER BY OrderDate DESC
""", (customer_id,))
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)
Los cambios estructurales clave son marcadores de parámetros, estilo de conexión y acceso a filas:
import mssql_python
def get_orders(customer_id: int):
conn = mssql_python.connect(
"Server=<server>;"
"Database=<database>;"
"UID=<username>;"
"PWD=<password>;"
)
cursor = conn.cursor()
cursor.execute("""
SELECT TOP 10 SalesOrderID, OrderDate, TotalDue
FROM Sales.SalesOrderHeader
WHERE CustomerID = ?
ORDER BY OrderDate DESC
""", (customer_id,))
orders = []
for row in cursor:
orders.append({
"id": row.SalesOrderID,
"date": row.OrderDate,
"total": row.TotalDue
})
cursor.close()
conn.close()
return orders
Los cambios estructurales son:
-
import pymssql→import mssql_python. - Argumentos posicionales de conexión → cadena de conexión con parámetros con nombre.
-
%dmarcador de parámetros →?. -
cursor(as_dict=True)→cursor()con acceso a atributos (row.SalesOrderIDen lugar derow["SalesOrderID"]).
Checklist
- [ ] Actualizar importaciones desde
pymssqlamssql_python. - [ ] Convertir llamadas de conexión de argumentos posicionales a cadenas de conexión.
- [ ] Convertir
%s/%dmarcadores de parámetros a?o .%(name)s - [ ] Usa
EXECUTEsentencias para llamadas a procedimientos almacenados. - [ ] Utiliza el acceso a atributos
Rowen lugar de los cursoresas_dict=True. - [ ] Migrar
conn.bulk_copy()acursor.bulkcopy(). - [ ] Eliminar la configuración de la agrupación externa de conexiones.
- [ ] Eliminar FreeTDS de los requisitos de despliegue.
- [ ] Actualizar los nombres de las clases de manejo de excepciones.
- [ ] Prueba todas las consultas y procedimientos almacenados.