NULL değerleri işle

SQL NULL, eksik veya bilinmeyen verileri temsil eder. mssql-python sürücüsü, SQL NULL'u Python'a Noneeşler. Bu ayrım önemlidir çünkü NULL hiçbir şeye, kendisi de dahil olmak üzere, eşit değildir. SQL'de ise NULL = NULL NULL (bilinmiyor) olarak değerlendirilir, doğru değil, bu yüzden sorgularda ve IS NULL Python'da kullanılıris None.

NULL değerleri alın

Getirme sonuçlarında NULL

Sürücü, SQL Server'dan NULL değerleri Python Noneolarak döndürür:

import mssql_python

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

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

print(row.FirstName)   # First name value
print(row.MiddleName)  # None (NULL in database)
print(row.LastName)    # Last name value

NULL değerleri kontrol edin

Sonuçlar arasında yinelerken, bir değerin None olup olmadığını is işleciyle denetleyin:

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

for row in cursor:
    if row.MiddleName is None:
        print(f"{row.FirstName} {row.LastName}: No middle name")
    else:
        print(f"{row.FirstName} {row.MiddleName} {row.LastName}")

is None yerine == None kullanma

Her zaman NULL kontrolleri için kullanın is None . is işleci özdeşliği denetler (değerin kelimenin tam anlamıyla None olup olmadığını), == ise __eq__'ü çağırır ve özel nesnelerde beklenmedik sonuçlar verebilir:

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

# Avoid (works but not idiomatic)
if row.MiddleName == None:
    full_name = f"{row.FirstName} {row.LastName}"

NULL değerler gönder

None ile NULL ekleyin

NULL değerleri eklemek için şu noktayı geçin None:

cursor.execute(
    "CREATE TABLE #NullInsertDemo "
    "(Name NVARCHAR(50), Email NVARCHAR(100), Phone NVARCHAR(20))"
)
cursor.execute(
    "INSERT INTO #NullInsertDemo (Name, Email, Phone) "
    "VALUES (%(name)s, %(email)s, %(phone)s)",
    {"name": "Alice", "email": None, "phone": "555-1234"}
)
conn.commit()

NULL olarak güncelle

Parametreleri aktararak bir sütunu NULL None olarak ayarlayın:

cursor.execute(
    "CREATE TABLE #UpdateDemo (ID INT, Email NVARCHAR(100))"
)
cursor.execute("INSERT INTO #UpdateDemo VALUES (100, 'old@example.com')")
cursor.execute(
    "UPDATE #UpdateDemo SET Email = %(email)s WHERE ID = %(id)s",
    {"email": None, "id": 100}
)
conn.commit()

Koşullu NULL işleme

İsteğe bağlı parametreler belirtilmediğinde bunları None olarak ayarlayan işlevler tanımlayın:

def update_record(cursor, record_id: int, name: str, email: str | None = None):
    """Update record, setting email to NULL if not provided."""
    cursor.execute(
        "UPDATE #Records SET Name = %(name)s, Email = %(email)s "
        "WHERE ID = %(id)s",
        {"name": name, "email": email, "id": record_id}
    )

WHERE cümlelerinde NULL

IS NULL sorgularda

NULL karşılaştırmaları için SQL'de IS NULL kullanın:

# Find people without a middle name
cursor.execute("SELECT FirstName FROM Person.Person WHERE MiddleName IS NULL")

# Find people with a middle name
cursor.execute("SELECT FirstName FROM Person.Person WHERE MiddleName IS NOT NULL")

Dinamik NULL işleme

Bir parametre NULL olabilirse, uygun sorguyu oluşturmak için koşullu mantık kullanın:

def find_people(cursor, middle_name: str | None = None):
    """Find people, optionally filtering by middle name."""
    if middle_name is None:
        # Find people with NULL middle name
        cursor.execute("SELECT * FROM Person.Person WHERE MiddleName IS NULL")
    else:
        # Find people with specific middle name
        cursor.execute(
            "SELECT * FROM Person.Person WHERE MiddleName = %(middle_name)s",
            {"middle_name": middle_name},
        )
    return cursor.fetchall()

NULL ikamesi için COALESCE

SQL seviyesinde varsayılan değerleri NULL yerine koymak için kullanılır COALESCE . COALESCEPython'da kontrol None etmekten daha verimli çünkü yerine geçme sunucuda gerçekleşir ve uygulamanızdaki koşullu mantık miktarını azaltır:

cursor.execute("""
    SELECT 
        FirstName,
        COALESCE(MiddleName, '(none)') AS MiddleName,
        COALESCE(Suffix, 'N/A') AS Suffix
    FROM Person.Person
    WHERE BusinessEntityID <= 10
""")

for row in cursor:
    # MiddleName and Suffix will never be None
    print(f"{row.FirstName}: {row.MiddleName}, {row.Suffix}")

NULL-güvenli işlemler

Python'da varsayılan değerler

cursor.execute("SELECT TOP 10 Name, Color FROM Production.Product")

for row in cursor:
    # Use or to provide default
    color = row.Color or "No color"
    print(f"{row.Name}: {color}")

NULL değerleri formatla

def format_address(row):
    """Format address handling NULL components."""
    parts = [
        row.AddressLine1,
        row.AddressLine2,
        row.City,
        row.PostalCode,
    ]
    # Filter out None values
    return ", ".join(str(p) for p in parts if p is not None)

cursor.execute(
    "SELECT TOP 10 AddressLine1, AddressLine2, City, PostalCode "
    "FROM Person.Address"
)
for row in cursor:
    print(format_address(row))

Toplulaştırmalarda NULL

SQL aggregate fonksiyonları, NULL değerleri beklediğinizden farklı şekilde işliyor. COUNT(column) yalnızca NULL olmayan değerleri sayarken COUNT(*) , tüm satırları sayar. AVG, SUM, MIN, ve MAX hepsi NULL değerleri görmezden gelir. Sütundaki her değer NULL ise, bu fonksiyonlar NULL döndürür (sıfır değil).

# COUNT excludes NULL values
cursor.execute("SELECT COUNT(Color) FROM Production.Product")  # Counts non-NULL colors
color_count = cursor.fetchval()

# COUNT(*) includes all rows
cursor.execute("SELECT COUNT(*) FROM Production.Product")  # Counts all products
total_count = cursor.fetchval()

# AVG ignores NULL
cursor.execute("SELECT AVG(Weight) FROM Production.Product")  # Average of non-NULL weights
average_weight = cursor.fetchval()

NULL ve veri tipleri

NULL sayısal değerler

from decimal import Decimal

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

# Check before arithmetic
if row.ListPrice is not None:
    tax = row.ListPrice * Decimal("0.08")
    total = row.ListPrice + tax
else:
    total = Decimal("0")

NULL tarih değerleri

Karşılaştırma veya hesaplamalarda kullanmadan önce bir tarih sütununun olup olmadığını None kontrol edin:

from datetime import date

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

for row in cursor:
    if row.SellEndDate is None:
        print(f"{row.Name}: Currently selling")
    else:
        print(f"{row.Name}: Discontinued on {row.SellEndDate}")

NULL dize değerleri

Birleştirmeden önce None denetleyerek NULL string sütunlarını işleyin:

cursor.execute(
    "SELECT TOP 10 FirstName, MiddleName, LastName FROM Person.Person"
)

for row in cursor:
    # Build full name, handling NULL middle name
    if row.MiddleName:
        full_name = f"{row.FirstName} {row.MiddleName} {row.LastName}"
    else:
        full_name = f"{row.FirstName} {row.LastName}"
    print(full_name)

NULL ile toplu işlemler

NULL değerleri ile executemany

executemany() kullanırken, NULL olması gereken sütunlar için sözlüklerde None geçirin:

users = [
    {"name": "Alice", "title": "Ms.", "suffix": "Jr."},
    {"name": "Bob", "title": None, "suffix": "Sr."},  # NULL title
    {"name": "Carol", "title": "Dr.", "suffix": None},  # NULL suffix
]

cursor.executemany(
    "SELECT FirstName FROM Person.Person WHERE FirstName = %(name)s",
    users
)

NULL ile toplu kopyalama

Toplu kopyalama işlemleri, veri yapılarınızdan NULL değerleri korur:

cursor = conn.cursor()

cursor.execute("CREATE TABLE ##NullDemo (Name NVARCHAR(50), Email NVARCHAR(100), Phone NVARCHAR(20))")
conn.commit()

data = [
    ("Alice", "alice@example.com", "555-0001"),
    ("Bob", None, "555-0002"),      # NULL Email
    ("Carol", "carol@example.com", None),  # NULL Phone
]

result = cursor.bulkcopy("##NullDemo", data)
conn.commit()
print(f"Copied {result['rows_copied']} rows")

Ortak desenler

Isteğe bağlı saha işleme

Satır sınıflarını veri sınıflarına eşlerken hangi alanların NULL olabileceğini netleştirmek için tip ipuçları kullanın:

from dataclasses import dataclass
from typing import Optional

@dataclass
class PersonRecord:
    business_entity_id: int
    first_name: str
    middle_name: Optional[str] = None
    suffix: Optional[str] = None

def fetch_person(cursor, person_id: int) -> Optional[PersonRecord]:
    cursor.execute(
        "SELECT BusinessEntityID, FirstName, MiddleName, Suffix "
        "FROM Person.Person WHERE BusinessEntityID = %(id)s",
        {"id": person_id},
    )
    row = cursor.fetchone()
    if row is None:
        return None
    return PersonRecord(
        business_entity_id=row.BusinessEntityID,
        first_name=row.FirstName,
        middle_name=row.MiddleName,  # Will be None if NULL
        suffix=row.Suffix,           # Will be None if NULL
    )

NULL ile JSON serileştirmesi

Python None değerleri modül kullanıldığında null otomatik olarak JSON'a json dönüştürülür:

import json

cursor.execute(
    "SELECT TOP 5 BusinessEntityID, FirstName, MiddleName FROM Person.Person"
)
rows = cursor.fetchall()

# Convert to JSON-serializable list
people = []
for row in rows:
    people.append({
        "id": row.BusinessEntityID,
        "name": row.FirstName,
        "middle_name": row.MiddleName,  # None becomes null in JSON
    })

json_output = json.dumps(people, indent=2)
print(json_output)
# [
#   {"id": 1, "name": "Ken", "middle_name": "J"},
#   {"id": 3, "name": "Roberto", "middle_name": null}
# ]

NULL filtreleme ile sözlük

Satır sözlüklere dönüştürülürken NULL değerleri hariç tutmayı tercih edin:

def row_to_dict(row, cursor) -> dict:
    """Convert row to dict, optionally excluding NULL values."""
    columns = [col[0] for col in cursor.description]
    return {col: val for col, val in zip(columns, row) if val is not None}

cursor.execute("SELECT * FROM Person.Person WHERE BusinessEntityID = 1")
row = cursor.fetchone()
person_dict = row_to_dict(row, cursor)
# Only includes non-NULL columns

DataFrames'te NULL

Pandas veya Polars DataFrame'lerle çalışırken, bu kütüphaneler kendi sentinel değerlerini kullandığı için null değerlere ekstra dikkat etmeniz gerekir.

pandalar NaN ve NaT

pandas, NaN eksik sayısal ve dize değerleri için (Not a Number) ve NaT eksik tarih ve zaman değerleri için (Not a Time) kullanır. Her iki değer de Python Noneile aynı değildir :

import pandas as pd
import numpy as np

# When reading SQL results into pandas, NULL becomes NaN or NaT
cursor.execute("SELECT Name, Weight, SellEndDate FROM Production.Product")
table = cursor.arrow()
df = table.to_pandas()

# Check for missing values (covers NaN, NaT, and None)
print(df["Weight"].isna().sum())       # Count of NULL weights
print(df["SellEndDate"].isna().sum())  # Count of NULL dates

# Stage the data in a temp table to avoid mutating the source table
cursor.execute("CREATE TABLE #ProductWeights (Name NVARCHAR(100), Weight DECIMAL(8, 2) NULL)")

# Convert NaN back to None so NULL values round-trip correctly
for _, row in df.iterrows():
    weight = None if pd.isna(row["Weight"]) else float(row["Weight"])
    cursor.execute(
        "INSERT INTO #ProductWeights (Name, Weight) VALUES (%(name)s, %(weight)s)",
        {"name": row["Name"], "weight": weight}
    )

Warning

== np.nan veya == pd.NaT ile karşılaştırmayın. Bu karşılaştırmalar her zaman False döndürür. Bunun yerine pd.isna() veya pd.notna() kullanın.

Polars boş değer işleme

Polars, doğrudan Python None ile eşlenen kendi null değerini (NaN değil) kullanır:

import polars as pl

cursor.execute("SELECT Name, Weight, Color FROM Production.Product")
table = cursor.arrow()
df = pl.from_arrow(table)

# Filter rows with non-null values
has_weight = df.filter(pl.col("Weight").is_not_null())

# Replace null with a default
df = df.with_columns(pl.col("Color").fill_null("No color"))