Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
SQL NULL mewakili data yang hilang atau tidak dikenal. Driver mssql-python memetakan SQL NULL ke Python None. Perbedaan itu penting karena NULL tidak sama dengan apa pun, termasuk dirinya sendiri. Dalam SQL, NULL = NULL mengevaluasi ke NULL (tidak diketahui), tidak benar, jadi gunakan IS NULL dalam kueri dan is None di Python.
Menerima nilai NULL
NULL dalam hasil pengambilan data
Driver mengembalikan nilai NULL dari SQL Server sebagai 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
Periksa nilai NULL
Periksa apakah suatu nilai adalah None menggunakan operator is saat melakukan iterasi melalui hasil:
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}")
Gunakan is None alih-alih == None
Selalu gunakan is None untuk pemeriksaan NULL. Operator is memeriksa identitas (apakah nilainya secara harfiah None), saat == memanggil __eq__ dan dapat memberikan hasil tak terduga dengan objek kustom:
# 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}"
Kirim nilai NULL
Sisipkan NULL dengan None
Untuk menyisipkan nilai NULL, berikan 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()
Perbarui ke NULL
Atur kolom menjadi NULL dengan memberikan None melalui parameter:
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()
Penanganan NULL bersyarat
Tentukan fungsi yang menangani parameter opsional dengan mengaturnya ke None saat tidak disediakan:
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 dalam klausa WHERE
IS NULL dalam kueri
Gunakan IS NULL dalam SQL untuk perbandingan 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")
Penanganan NULL dinamis
Ketika parameter mungkin NULL, gunakan logika bersyarat untuk membuat kueri yang sesuai:
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 untuk substitusi nilai NULL
Gunakan COALESCE untuk mengganti nilai default untuk NULL di tingkat SQL.
COALESCElebih efisien daripada memeriksa None di Python karena penggantian terjadi di server, mengurangi jumlah logika bersyarat dalam aplikasi Anda:
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}")
Operasi NULL-aman
Nilai default di Python
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}")
Memformat nilai 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 dalam agregasi
Fungsi agregat SQL menangani nilai NULL secara berbeda dari yang Anda harapkan.
COUNT(column) hanya menghitung nilai non-NULL, sementara COUNT(*) menghitung semua baris.
AVG, SUM, , MINdan MAX semua mengabaikan nilai NULL. Jika setiap nilai dalam kolom adalah NULL, fungsi ini mengembalikan NULL (bukan nol).
# 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 pada tipe data
Nilai numerik 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")
Nilai tanggal NULL
Periksa apakah kolom tanggal bernilai None sebelum menggunakannya dalam perbandingan atau perhitungan:
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}")
Nilai string NULL
Tangani kolom string NULL dengan memeriksa None sebelum menggabungkan:
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)
Operasi massal dengan NULL
executemany dengan nilai NULL
Saat menggunakan executemany(), sertakan None dalam kamus untuk kolom yang harus bernilai 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
)
Salin massal dengan NULL
Operasi penyalinan massal mempertahankan nilai NULL dari struktur data Anda:
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")
Pola umum
Pemrosesan kolom opsional
Gunakan petunjuk jenis untuk memperjelas bidang mana yang dapat menjadi NULL saat memetakan baris ke kelas data:
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
)
Serialisasi JSON dengan NULL
Nilai Python None secara otomatis dikonversi ke JSON null saat menggunakan json modul:
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}
# ]
Kamus dengan pemfilteran NULL
Pilih untuk mengecualikan nilai NULL saat mengonversi baris ke kamus:
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 dalam DataFrames
Saat Anda bekerja dengan panda atau Polars DataFrames, Anda perlu memberikan perhatian ekstra pada nilai null karena pustaka ini menggunakan nilai sentinelnya sendiri.
panda NaN dan NaT
pandas menggunakan NaN (Bukan Angka) untuk nilai numerik dan string yang hilang, dan NaT (Bukan Waktu) untuk nilai tanggalwaktu yang hilang. Kedua nilai tidak sama dengan Python None:
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
Jangan membandingkan dengan == np.nan atau == pd.NaT. Perbandingan ini selalu mengembalikan False. Gunakan pd.isna() atau pd.notna() sebagai gantinya.
Penanganan null di Polars
Polars menggunakan nilai null miliknya sendiri (bukan NaN), yang langsung dipetakan ke 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"))