Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
O controlador mssql-python é o controlador Python oficial da Microsoft para o Microsoft SQL. Se preferir uma opção de driver mantido pela Microsoft, oferece:
- Sem dependência de drivers ODBC externos.
- Agrupamento de ligações integrado.
- Suporte moderno para Python 3.10+.
- Autenticação nativa da Microsoft Entra.
Diferenças principais
| Feature | pyodbc | mssql-python |
|---|---|---|
| Estilo de parâmetros |
qmark (?) |
qmark (?) e pyformat (%(name)s) |
| É necessário um driver ODBC | Sim | No |
| Agrupamento de conexões | Externo | Built-in |
| Versão mínima do Python | 3.6 | 3,10 |
callproc() |
Suportado | Não implementado |
| Confirmação automática predefinida | Off | Off |
Etapas básicas de migração
Os passos seguintes cobrem as principais alterações para migrar uma aplicação pyodbc para mssql-python.
1. Atualizar importações
Substitua a pyodbc importação por mssql_python:
Antes (pyodbc):
import pyodbc
Depois (mssql-python):
import mssql_python
2. Atualizar cadeias de ligação
Remover a DRIVER= palavra-chave e atualizar o método de autenticação:
Antes (pyodbc, requer o driver ODBC):
conn = pyodbc.connect(
"DRIVER={ODBC Driver 18 for SQL Server};"
"SERVER=localhost;"
"DATABASE=AdventureWorks2022;"
"Trusted_Connection=yes;"
)
Depois (mssql-python, sem necessidade de driver, usando autenticação do Microsoft Entra):
conn = mssql_python.connect(
"Server=<server>.database.windows.net;"
"Database=<database>;"
"Authentication=ActiveDirectoryDefault;"
"Encrypt=yes;"
)
3. Mantenha as suas consultas tal como estão
O driver mssql-python suporta os estilos de parâmetros ? (qmark) e %(name)s (pyformat). As suas consultas existentes ? funcionam sem alterações:
Antes (pyodbc):
cursor.execute("SELECT * FROM Production.Product WHERE ProductID = ? AND Color = ?", (1, "Red"))
Depois (mssql-python, mesma consulta):
cursor.execute("SELECT * FROM Production.Product WHERE ProductID = ? AND Color = ?", (1, "Red"))
4. Mantenha executemany como está
As chamadas existentes executemany com tuplos e marcadores ? continuam a funcionar sem alterações:
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)
Depois (mssql-python, mesmo 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)
Migração de procedimentos armazenados
O driver mssql-python não implementa callproc(). As secções seguintes mostram como usar EXECUTE em vez disso.
Utilize EXECUTE para procedimentos armazenados
O driver pyodbc suporta callproc(), mas o driver mssql-python não. Use EXECUTE em vez disso:
Antes (pyodbc):
cursor.callproc("dbo.uspGetEmployeeManagers", (5,))
results = cursor.fetchall()
Depois (mssql-python):
cursor.execute(
"EXECUTE dbo.uspGetEmployeeManagers @BusinessEntityID = %(id)s",
{"id": 5}
)
results = cursor.fetchall()
print(f"Got {len(results)} rows")
Parâmetros de saída
Use variáveis T-SQL para capturar valores de saída em vez de depender de callproc() parâmetros de saída:
Antes (pyodbc, usando callproc):
params = (category_id, pyodbc.SQL_INTEGER)
cursor.callproc("dbo.GetProductCount", params)
count = params[1].value
Depois (mssql-python, usando variáveis 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}")
Migrações específicas de funcionalidades
As secções seguintes cobrem funcionalidades específicas do pyodbc e os seus equivalentes mssql-python.
Cadeias de ligação
| Palavra-chave pyodbc | Palavra-chave mssql-python | Notes |
|---|---|---|
DRIVER={...} |
Não necessário | O driver ODBC é incluído internamente. |
SERVER= |
Server= |
Sem mudança de comportamento. |
DATABASE= |
Database= |
Sem mudança de comportamento. |
Trusted_Connection= |
Trusted_Connection= |
Sem mudança de comportamento. |
UID= / PWD= |
UID= / PWD= |
Sem mudança de comportamento. |
Authentication= |
Authentication= |
Aceita os mesmos valores. |
Confirmação automática
O comportamento do autocommit é idêntico em ambos os drivers:
pyodbc:
conn.autocommit = True
pyodbc.connect(connection_string, autocommit=True)
mssql-python:
conn.autocommit = True
Inserções em massa
Para acelerar grandes lotes de INSERT, os utilizadores de pyodbc configuram fast_executemany = True. O driver mssql-python já otimiza executemany para lotes parametrizados, por isso inserções moderadas não precisam de flag especial. Para cargas de dados grandes, prefira bulkcopy(), que transmite linhas através do protocolo de cópia em massa e é muito mais rápido do que emitir instruções individuais INSERT . Para o fluxo de trabalho completo, veja Usar cópia em massa.
pyodbc:
cursor.fast_executemany = True
cursor.executemany(query, data)
Após (mssql-python), faz lotes moderados com 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()
Após (mssql-python), cargas de grande volume com bulkcopy (preferencial):
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 linhas
O controlador mssql-python retorna objetos Row que permitem o acesso a atributos por predefinição, sem exigir uma função de criação de linhas personalizada:
PYODBC (Custom Row Factory):
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 (acesso a atributos por defeito):
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
Tratamento de erros
O driver mssql-python usa a mesma hierarquia de exceções do pyodbc, pelo que a maioria dos tratadores de exceções requer apenas a alteração do nome do módulo.
Hierarquia de exceção
Os nomes das classes de exceção são mapeados diretamente entre os drivers:
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
Detalhes do erro
Ambos os controladores expõem detalhes de erro através dos argumentos da exceção:
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))
Agrupamento de conexões
O controlador mssql-python inclui agrupamento de ligações por predefinição, pelo que as bibliotecas externas para agrupamento de ligações já não são necessárias.
Remover a agregação externa
Se usaste pooling externo com pyodbc, o driver mssql-python tem isso incorporado:
Antes (piscina externa 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()
Após (com pool de ligações incorporado no mssql-python):
conn = mssql_python.connect(connection_string)
conn.close()
Configurar agrupamento
Redefina o tamanho predefinido do pool e o timeout com mssql_python.pooling():
import mssql_python
mssql_python.pooling()
Exemplo completo de migração
O seguinte mostra a mesma função escrita com pyodbc e depois reescrita com mssql-python.
Antes (pyodbc)
Esta versão utiliza a cadeia de ligação pyodbc com uma palavra-chave DRIVER:
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
Depois (mssql-python)
Esta versão remove a DRIVER palavra-chave. Todas as consultas, parâmetros e padrões de acesso à linha permanecem 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
As únicas alterações são a instrução de importação e a cadeia de ligação (sem DRIVER necessidade de palavra-chave). Cada consulta, parâmetro, padrão de obtenção e acesso a linhas permanece idêntico.
Teste a migração
Antes de concluir a migração, execute as mesmas consultas em ambos os drivers e compare os resultados para confirmar o comportamento equivalente.
Verificar comportamento equivalente
Use uma função de comparação que execute a mesma consulta contra ambos os drivers e afirme que os resultados coincidem:
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")
Lista de Verificação
- [ ] Atualizar importações de
pyodbcparamssql_python. - [ ] Retirar
DRIVER=dos fios de ligação. - [ ] Mantém as consultas de parâmetros existentes
?(funcionam as-is). - [ ] Utilize instruções
EXECUTEpara chamadas a procedimentos armazenados. - [ ] Remover a configuração de agrupamento externo de ligações.
- [ ] Atualizar os nomes das classes de tratamento de exceções.
- [ ] Testar todas as consultas e procedimentos armazenados.
- [ ] Verificar o tratamento dos tipos de dados (especialmente decimais e datas).
- [ ] Remover o driver ODBC dos requisitos de implementação.