Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
SQL NULL reprezentuje brakujące lub nieznane dane. Sterownik mssql-python mapuje SQL NULL na PythonNone. To rozróżnienie ma znaczenie, ponieważ NULL nie oznacza niczego, włącznie z samym sobą. W SQL NULL = NULL daje wartość NULL (nieznane), a nie true, więc w zapytaniach używaj IS NULL, a w Pythonie is None.
Otrzymuj wartości NULL
NULL w wynikach pobierania
Sterownik zwraca wartości NULL z SQL Server jako Python None:
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
Sprawdź, czy są wartości NULL
Sprawdź, czy wartość jest None, używając operatora is podczas iteracji po wynikach:
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}")
Użyj is None zamiast == None
Zawsze używaj is None do sprawdzania NULL. Operator is sprawdza tożsamość (czy wartość jest dosłownie None), podczas gdy == wywołuje __eq__ i może dawać nieoczekiwane wyniki w przypadku obiektów niestandardowych:
# 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}"
Wyślij wartości NULL
Wstaw NULL przy użyciu None
Aby wstawić wartości NULL, przekaż 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()
Aktualizacja do NULL
Ustaw kolumnę na NULL, przekazując wartość None w parametrach:
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()
Warunkowa obsługa NULL
Zdefiniuj funkcje obsługujące opcjonalne parametry, ustawiając je na, None gdy nie są dostępne:
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}
)
NULL w klauzulach WHERE
IS NULL w zapytaniach
Zastosowanie IS NULL w SQL do porównań NULL:
# 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")
Dynamiczne obsługiwanie NULL
Gdy parametr może być NULL, użyj logiki warunkowej do skonstruowania odpowiedniego zapytania:
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()
COALESCE do zastępowania wartości NULL
Użyj COALESCE, aby podstawiać wartości domyślne w miejsce NULL na poziomie SQL.
COALESCE jest bardziej efektywne niż sprawdzanie elementu None w języku Python, ponieważ zastępowanie odbywa się na serwerze, co zmniejsza liczbę instrukcji warunkowych w aplikacji:
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}")
Operacje bezpieczne dla NULL
Domyślne wartości w Pythonie
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}")
Formatuj wartości NULL
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))
NULL w agregacjach
Funkcje agregatora SQL obsługują wartości NULL inaczej, niż można by się spodziewać.
COUNT(column) liczy tylko wartości nie-NULL, a COUNT(*) wszystkie wiersze.
AVG, SUM, MIN, i MAX wszystkie ignorują wartości NULL. Jeśli każda wartość w kolumnie jest NULL, funkcje te zwracają NULL (nie zero).
# 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 z typami danych
Wartości numeryczne NULL
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")
Wartości daty NULL
Sprawdź, czy kolumna z datami ma wartość None, zanim użyjesz jej w porównaniach lub obliczeniach:
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}")
Wartości ciągów NULL
Obsłuż kolumny ciągów znaków o wartości NULL, sprawdzając None przed konkatenacją:
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)
Operacje masowe z NULL
executemany z wartościami NULL
Korzystając z executemany(), przekaż None w słownikach dla kolumn, które powinny mieć wartość NULL:
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
)
Kopiowanie zbiorcze z NULL
Operacje kopiowania masowego zachowują wartości NULL ze struktur danych:
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")
Często używane wzorce
Opcjonalne przetwarzanie terenowe
Użyj podpowiedzi typu, aby wyjaśnić, które pola mogą być NULL podczas mapowania wierszy na klasy danych:
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
)
Serializacja JSON z wartością NULL
Wartości Python None automatycznie konwertują do JSON null podczas korzystania z modułujson:
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}
# ]
Słownik z filtrowaniem NULL
Wyłącz wartości NULL podczas konwersji wierszy na słowniki:
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
NULL w ramkach danych
Pracując z pandas lub Polars DataFrames, musisz zwracać szczególną uwagę na wartości zerowe, ponieważ te biblioteki używają własnych wartości sentinelowych.
Pandas NaN i NaT
Pandas używa NaN (Not a Number) dla brakujących wartości liczbowych i ciągów tekstowych oraz NaT (Not a Time) dla brakujących wartości date-time. Żadna z tych wartości nie jest taka sama jak w PythonieNone:
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
Nie porównuj z == np.nan lub == pd.NaT. Te porównania zawsze zwracają False. Użyj polecenia pd.isna() lub pd.notna() zamiast tego.
Obsługa wartości null w Polars
Polars używa własnej wartości null (a nie NaN), która odpowiada bezpośrednio wartości Python None:
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"))