Récupérer des données avec mssql-python

Le pilote mssql-python propose plusieurs méthodes de récupération, des schémas d’accès aux lignes et des fonctionnalités de navigation par curseur pour récupérer les résultats des requêtes.

Méthodes de récupération

Après avoir exécuté une requête SELECT, utilisez des méthodes de récupération pour récupérer les résultats.

Fetchone()

Retourne une seule ligne ou None si aucune autre ligne n’est disponible :

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

Ça renvoie une liste de lignes. cursor.arraysize Contrôle la taille par défaut du lot (par défaut : 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)

Vous pouvez également spécifier directement la taille :

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

fetchall()

Retourne toutes les lignes restantes sous forme de liste :

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

Retourne la première colonne de la première ligne, ce qui est utile pour les requêtes scalaires.

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

Modèles d’accès aux lignes

La Row classe prend en charge plusieurs modèles d’accès.

Accès à l’index

Accéder aux colonnes par position (base zéro) :

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]

Accès aux attributs

Accéder aux colonnes par nom :

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

Noms de colonnes en minuscules

Activez les noms d’attributs minuscules à l’échelle mondiale :

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

Itération

Les lignes permettent d’itérer sur les valeurs :

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

for value in row:
    print(value)

Itération du curseur

Itère directement sur le curseur pour traiter les lignes :

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

for row in cursor:
    print(row.Name)

Ce schéma équivaut à des appels fetchone() répétés.

Métadonnées de colonne

Accéder aux informations de colonne via 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}")

Nombre de lignes

L’attribut cursor.rowcount indique :

  • Pour SELECT : retourne -1 après execute(), jusqu’au début de l’extraction. Une fois que vous lancez la récupération, cette valeur indique le nombre cumulé de lignes récupérées jusqu’à présent.
  • Pour INSERT/UPDATE/DELETE : Nombre de lignes affectées.
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}")

Navigation par curseur

skip()

Sauter des rangées sans les chercher :

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

scroll()

Avancez la position du curseur :

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

Le pilote ne supporte qu’avec mode='relative' des valeurs positives. Le positionnement absolu et le défilement arrière sont élevés NotSupportedError car le pilote utilise uniquement des curseurs avant.

Numéro de rangée

Suivez la position actuelle :

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)

Ensembles de résultats multiples

À utiliser nextset() pour traiter plusieurs ensembles de résultats :

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

Grands ensembles de résultats

Pour de grands ensembles de résultats, traitez les lignes par lots afin de gérer la mémoire :

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

Gestionnaires de contexte

Utilisez des gestionnaires de contexte pour le nettoyage automatique des ressources :

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

Bonnes pratiques

  • Utilisez fetchmany() pour obtenir de gros résultats afin d’éviter de tout charger en mémoire.
  • Fermez les curseurs une fois terminé pour libérer les ressources du serveur.
  • Utilisez les noms des colonnes (accès aux attributs) pour un code plus lisible.
  • Vérifiez rowcount après les instructions de modification de données.
  • Gérer None explicitement les valeurs lorsque les colonnes sont nullables.