Objek baris di mssql-python

Driver mssql-python mengembalikan data baris sebagai Row objek dari operasi pengambilan. Objek ini menyediakan pola akses yang fleksibel:

  • Akses atribut (row.ColumnName) adalah pilihan yang paling mudah dibaca untuk kolom bernama. Gunakan saat kueri Anda memiliki daftar kolom yang diketahui dan stabil.
  • Akses kunci string (row['ColumnName']) menyediakan akses gaya kamus ke kolom berdasarkan nama, berguna saat Anda memerlukan pencarian kolom terprogram atau ketika nama kolom berisi spasi atau karakter khusus.
  • Akses indeks (row[0]) berguna untuk kueri dinamis di mana nama kolom tidak diketahui pada waktu pengembangan, atau saat memproses SELECT * hasil.
  • Pembongkaran tuple (a, b, c = row) adalah pilihan paling ringkas untuk badan loop dengan jumlah kolom yang kecil dan tetap.

Akses atribut

Mengakses nilai kolom secara langsung berdasarkan nama:

import mssql_python

conn = mssql_python.connect(connection_string)
cursor = conn.cursor()

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

print(row.ProductID)     # 1
print(row.Name)          # 'Adjustable Race'
print(row.ListPrice)     # 0.00

Peka huruf besar/kecil

Akses nama kolom peka huruf besar/kecil dan cocok dengan nama kolom yang dikembalikan oleh SQL Server. Jika database Anda menggunakan casing yang tidak konsisten, gunakan alias SQL AS untuk menormalkan nama, atau aktifkan lowercase pengaturan modul (lihat Konfigurasi modul):

cursor.execute("SELECT FirstName, LastName, EmailPromotion FROM Person.Person WHERE BusinessEntityID < 10")
row = cursor.fetchone()

print(row.FirstName)      # Works
print(row.LastName)       # Works
print(row.EmailPromotion) # Works
print(row.firstname)      # AttributeError - wrong case

Alias kolom

Gunakan alias SQL untuk membuat nama atribut yang ramah:

cursor.execute("""
    SELECT 
        p.ProductID,
        p.Name,
        c.Name AS Category
    FROM Production.Product p
    JOIN Production.ProductSubcategory c ON p.ProductSubcategoryID = c.ProductSubcategoryID
    WHERE p.ProductSubcategoryID IS NOT NULL
""")

for row in cursor:
    print(f"{row.Name} ({row.Category})")

Akses indeks

Nilai akses dengan indeks kolom berbasis nol:

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

print(row[0])  # ProductID
print(row[1])  # Name
print(row[2])  # ListPrice

Pengindeksan negatif

Driver mendukung pengindeksan negatif gaya Python:

cursor.execute("""
    SELECT Demo.A, Demo.B, Demo.C, Demo.D
    FROM (VALUES (1, 2, 3, 4)) AS Demo(A, B, C, D)
""")
row = cursor.fetchone()

print(row[-1])  # Last column (D)
print(row[-2])  # Second to last (C)

Akses kunci string

Akses nilai kolom berdasarkan nama menggunakan sintaks gaya kamus:

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

print(row['ProductID'])   # 1
print(row['Name'])        # 'Adjustable Race'
print(row['ListPrice'])   # Decimal('0.00')

Ini berguna ketika nama kolom berisi spasi atau karakter khusus, atau saat Anda perlu mengakses kolom secara terprogram:

column_name = 'ListPrice'
value = row[column_name]  # Programmatic column access

Membelah

Ekstrak beberapa nilai menggunakan irisan:

cursor.execute("""
    SELECT Demo.A, Demo.B, Demo.C, Demo.D, Demo.E
    FROM (VALUES (1, 2, 3, 4, 5)) AS Demo(A, B, C, D, E)
""")
row = cursor.fetchone()

print(row[1:4])    # Columns B, C, D (indices 1, 2, 3)
print(row[:2])     # First two columns (A, B)
print(row[2:])     # From C to end

Pembongkaran tuple

Membongkar nilai baris langsung ke dalam variabel:

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

for product_id, name, price in cursor:
    print(f"#{product_id}: {name} - ${price}")

Pembongkaran sebagian

Gunakan * untuk menangkap nilai yang tersisa:

cursor.execute("""
    SELECT TOP 2 ProductID, Name, ProductNumber, Color, Size, Weight
    FROM Production.Product
    WHERE ProductNumber IS NOT NULL
    ORDER BY ProductID
""")

for product_id, name, *rest in cursor:
    print(f"{product_id}: {name}, extra columns: {rest}")

Panjang baris dan iterasi

Bekerja dengan dimensi baris dan menulang nilai kolom:

Mendapatkan jumlah kolom

Gunakan len() untuk mendapatkan jumlah kolom dalam satu baris:

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

print(len(row))  # Number of columns

Iterasi nilai

Perulangkan nilai kolom secara berurutan:

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

for value in row:
    print(value)

Periksa apakah kolom ada

Gunakan hasattr() untuk menguji keberadaan nama kolom:

# Use hasattr to check for column name
if hasattr(row, 'DiscountPrice'):
    print(f"Discount: {row.DiscountPrice}")
else:
    print("No discount available")

Konversi ke jenis bawaan

Objek baris dapat dikonversi ke jenis Python standar untuk integrasi dengan pustaka dan API lain:

Konversi ke tuple

Konversi baris menjadi tuple menggunakan tuple() konstruktor:

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

row_tuple = tuple(row)
print(row_tuple)  # (1, 'Adjustable Race')

Konversi ke daftar

Mengonversi baris menjadi daftar menggunakan list() konstruktor:

row_list = list(row)
print(row_list)  # [1, 'Adjustable Race']

Ubah ke bentuk kamus

Konversi baris ke kamus saat Anda perlu menserialisasikannya ke JSON, meneruskannya ke mesin templat, atau menggabungkannya dengan data lain. Buat kamus dari cursor.description dan nilai baris:

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

# Create dict from description and values
columns = [col[0] for col in cursor.description]
row_dict = dict(zip(columns, row))
print(row_dict)  # {'ProductID': 1, 'Name': 'Adjustable Race', 'ListPrice': Decimal('0.00')}

Fungsi pembantu untuk konversi dikte

Buat fungsi pembantu yang dapat digunakan kembali untuk mengonversi semua baris yang diambil menjadi kamus:

def rows_to_dicts(cursor):
    """Convert fetched rows to list of dictionaries."""
    columns = [col[0] for col in cursor.description]
    return [dict(zip(columns, row)) for row in cursor.fetchall()]

cursor.execute("SELECT ProductID, Name, ListPrice FROM Production.Product WHERE ProductID < 10")
products = rows_to_dicts(cursor)
for p in products:
    print(p['Name'])

Bekerja dengan nilai null

Driver mengembalikan nilai NULL sebagai Python None:

cursor.execute("SELECT FirstName, MiddleName, LastName FROM Person.Person WHERE BusinessEntityID = 1")
row = cursor.fetchone()

if row.MiddleName is None:
    full_name = f"{row.FirstName} {row.LastName}"
else:
    full_name = f"{row.FirstName} {row.MiddleName} {row.LastName}"

Bekerja dengan cursor.description

Mengakses metadata kolom bersama data baris:

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

# Column information
for col in cursor.description:
    print(f"Name: {col[0]}, Type: {col[1]}")

# Fetch with metadata
row = cursor.fetchone()
for i, col in enumerate(cursor.description):
    print(f"{col[0]}: {row[i]}")

Pola umum

Berikut adalah pola praktis untuk bekerja dengan objek Baris dalam aplikasi dunia nyata:

Baris proses dengan akses bernama

Memproses baris menggunakan akses atribut untuk kode yang dapat dibaca dan dipelihara:

def process_orders(conn):
    cursor = conn.cursor()
    cursor.execute("""
        SELECT SalesOrderID, CustomerID, OrderDate, TotalDue 
        FROM Sales.SalesOrderHeader 
        WHERE Status = 5
    """)
    
    for order in cursor:
        print(f"Order #{order.SalesOrderID}")
        print(f"  Customer: {order.CustomerID}")
        print(f"  Date: {order.OrderDate}")
        print(f"  Total: ${order.TotalDue:.2f}")

Membangun objek dari baris

Untuk aplikasi dengan model domain, petakan baris ke kelas data atau objek yang diketik. Pemetaan ini memberi Anda pelengkapan otomatis IDE, pemeriksaan jenis, dan batas yang jelas antara baris database dan logika aplikasi.

from dataclasses import dataclass
from datetime import date
from decimal import Decimal

@dataclass
class Product:
    id: int
    name: str
    price: Decimal
    created: date

def get_products(conn) -> list[Product]:
    cursor = conn.cursor()
    cursor.execute("SELECT ProductID, Name, ListPrice, SellStartDate FROM Production.Product WHERE ProductID < 10")
    
    return [
        Product(
            id=row.ProductID,
            name=row.Name,
            price=row.ListPrice,
            created=row.SellStartDate
        )
        for row in cursor
    ]

Ekspor ke JSON

Serialisasi objek Baris ke JSON dengan penanganan jenis kustom untuk nilai tanggalwaktu dan Desimal:

import json
from datetime import date, datetime
from decimal import Decimal

def json_serializer(obj):
    """Custom serializer for non-JSON types."""
    if isinstance(obj, (date, datetime)):
        return obj.isoformat()
    if isinstance(obj, Decimal):
        return float(obj)
    raise TypeError(f"Type {type(obj)} not serializable")

def export_to_json(cursor, filename):
    columns = [col[0] for col in cursor.description]
    rows = [dict(zip(columns, row)) for row in cursor.fetchall()]
    
    with open(filename, 'w') as f:
        json.dump(rows, f, default=json_serializer, indent=2)

cursor.execute("SELECT ProductID, Name, ListPrice FROM Production.Product WHERE ProductID < 10")
export_to_json(cursor, "products.json")

Agregasi ke dalam grup

Kelompokkan baris berdasarkan nilai kolom dan kumpulkan ke dalam kamus untuk analisis atau tampilan:

from collections import defaultdict

cursor.execute("""
    SELECT c.Name AS CategoryName, p.Name AS ProductName, p.ListPrice 
    FROM Production.Product p
    JOIN Production.ProductSubcategory c ON p.ProductSubcategoryID = c.ProductSubcategoryID
    WHERE p.ProductSubcategoryID IS NOT NULL
    ORDER BY c.Name
""")

products_by_category = defaultdict(list)
for row in cursor:
    products_by_category[row.CategoryName].append({
        'name': row.ProductName,
        'price': row.ListPrice
    })

for category, products in products_by_category.items():
    print(f"\n{category}:")
    for p in products:
        print(f"  - {p['name']}: ${p['price']}")