Recuperar dados com mssql-python

O driver mssql-python oferece vários métodos de busca, padrões de acesso a linhas e funcionalidades de navegação de cursor para recuperar resultados de consultas.

Métodos de obtenção

Após executar uma consulta SELECT, use métodos fetch para obter resultados.

Fetchone()

Devolve uma única linha ou None , se não houver mais linhas disponíveis:

cursor.execute("SELECT ProductID, Name, ListPrice FROM Production.Product")

row = cursor.fetchone()
while row:
    print(f"{row.ProductID}: {row.Name} - ${row.ListPrice}")
    row = cursor.fetchone()

fetchmany()

Devolve uma lista de linhas. cursor.arraysize Controla o tamanho padrão do lote (padrão: 1):

cursor.execute("SELECT * FROM Production.Product")
cursor.arraysize = 100  # Fetch 100 rows at a time

while True:
    rows = cursor.fetchmany()
    if not rows:
        break
    for row in rows:
        print(row.Name)

Também pode especificar o tamanho diretamente:

rows = cursor.fetchmany(50)  # Fetch up to 50 rows

fetchall()

Devolve todas as linhas restantes como uma lista:

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

print(f"Found {len(rows)} products")
for row in rows:
    print(row.Name)

Fetchval()

Devolve a primeira coluna da primeira linha, o que é útil para consultas escalares.

count = cursor.execute("SELECT COUNT(*) FROM Production.Product").fetchval()
print(f"Total products: {count}")

max_price = cursor.execute("SELECT MAX(ListPrice) FROM Production.Product").fetchval()
print(f"Highest price: ${max_price}")

Padrões de acesso de linha

A Row classe suporta múltiplos padrões de acesso.

Acesso ao índice

Aceder às colunas por posição (base zero):

cursor.execute("SELECT ProductID, Name, ListPrice FROM Production.Product WHERE ProductID < 5")
row = cursor.fetchone()

product_id = row[0]
name = row[1]
price = row[2]

Acesso a atributos

Aceder às colunas pelo nome:

cursor.execute("SELECT ProductID, Name, ListPrice FROM Production.Product WHERE ProductID < 5")
row = cursor.fetchone()

product_id = row.ProductID
name = row.Name
price = row.ListPrice

Nomes de colunas em minúsculas

Ativar nomes de atributos minúsculos globalmente:

import mssql_python

settings = mssql_python.get_settings()
settings.lowercase = True

cursor.execute("SELECT ProductID, Name FROM Production.Product WHERE ProductID < 5")
row = cursor.fetchone()
print(row.productid, row.name)  # Lowercase access
settings.lowercase = False  # Restore default

Iteration

As linhas suportam iteração sobre valores:

cursor.execute("SELECT ProductID, Name, ListPrice FROM Production.Product WHERE ProductID < 5")
row = cursor.fetchone()

for value in row:
    print(value)

Iteração do cursor

Iterar diretamente sobre o cursor para processar linhas:

cursor.execute("SELECT * FROM Production.Product")

for row in cursor:
    print(row.Name)

Este padrão equivale a chamar fetchone() repetidamente.

Metadados das colunas

Aceder à informação das colunas através de cursor.description:

cursor.execute("SELECT ProductID, Name, ListPrice FROM Production.Product WHERE ProductID < 5")

for col in cursor.description:
    name, type_code, display_size, internal_size, precision, scale, null_ok = col
    print(f"Column: {name}, Type: {type_code}, Nullable: {null_ok}")

Contagem de linhas

O cursor.rowcount atributo indica:

  • Para SELECT: Devolve -1 após execute() até que se inicie a obtenção de dados. Assim que começa a obter dados, reflete o número cumulativo de linhas obtidas até ao momento.
  • Para INSERT/UPDATE/DELETE: Número de linhas afetadas.
cursor.execute("SELECT * FROM Production.Product")
print(f"Rows returned: {cursor.rowcount}")

cursor.execute("CREATE TABLE #PriceUpd (Name NVARCHAR(50), Price DECIMAL(10,2), CategoryID INT)")
cursor.execute("INSERT INTO #PriceUpd VALUES ('A',10,1),('B',20,1),('C',30,2)")
cursor.execute("UPDATE #PriceUpd SET Price = Price * 1.1 WHERE CategoryID = 1")
print(f"Rows updated: {cursor.rowcount}")

Navegação por cursor

skip()

Salta as filas sem as buscar:

cursor.execute("SELECT * FROM Production.Product ORDER BY ProductID")
cursor.skip(10)  # Skip first 10 rows
row = cursor.fetchone()  # Returns 11th row

scroll()

Avance a posição do cursor:

cursor.execute("SELECT * FROM Production.Product ORDER BY ProductID")

# Move forward 5 rows from current position
cursor.scroll(5, mode='relative')

row = cursor.fetchone()

Note

O controlador suporta apenas mode='relative' com valores positivos. O posicionamento absoluto e o deslocamento para trás originam NotSupportedError porque o controlador usa cursores somente para a frente.

Número da linha

Acompanhe a posição atual:

cursor.execute("SELECT * FROM Production.Product")

print(f"Initial position: {cursor.rownumber}")  # -1 (before first fetch)

row = cursor.fetchone()
print(f"After fetchone: {cursor.rownumber}")    # 0 (first row fetched)

Vários conjuntos de resultados

Uso nextset() para processar múltiplos conjuntos de resultados:

cursor.execute("""
    SELECT * FROM Production.Product WHERE Color = 'Black';
    SELECT * FROM Production.ProductCategory;
    SELECT COUNT(*) FROM Production.Product;
""")

# First result set
products = cursor.fetchall()
print(f"Products: {len(products)}")

# Move to second result set
if cursor.nextset():
    categories = cursor.fetchall()
    print(f"Categories: {len(categories)}")

# Move to third result set
if cursor.nextset():
    count = cursor.fetchval()
    print(f"Total count: {count}")

Grandes conjuntos de resultados

Para grandes conjuntos de resultados, processe linhas em lotes para gerir a memória:

def process_batch(rows):
    # Example: print each row. Replace with your own logic.
    for row in rows:
        print(row)

cursor.execute("SELECT * FROM LargeTable")
cursor.arraysize = 1000

while True:
    rows = cursor.fetchmany()
    if not rows:
        break

    process_batch(rows)
    print(f"Processed {cursor.rownumber} rows so far")

Gestores de contexto

Use gestores de contexto para limpeza automática de recursos:

with mssql_python.connect(connection_string) as conn:
    with conn.cursor() as cursor:
        cursor.execute("SELECT * FROM Production.Product")
        for row in cursor:
            print(row.Name)
# Cursor and connection closed automatically

Melhores práticas

  • Use fetchmany() para resultados grandes para evitar carregar tudo na memória.
  • Fecha os cursores quando terminar para libertar recursos do servidor.
  • Use nomes de colunas (acesso a atributos) para um código mais legível.
  • CHECK rowcount após instruções de modificação de dados.
  • Manipule None os valores explicitamente quando as colunas são anuláveis.