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.
Kolumny rzadkie to optymalizacja przechowywania danych Microsoft SQL dla wartości NULL w tabelach z wieloma kolumnami do zerowania. Aplikacje klienckie postrzegają rzadkie kolumny jako kolumny regularne. Sterownik mssql-python odczytuje i zapisuje je jak każdą inną kolumnę, bez specjalnego obsługiwania.
| Funkcja | Opis |
|---|---|
| Rzadkie kolumny | Wartości NULL nie używają pamięci |
| Zestawy kolumn | Reprezentacja XML wszystkich rzadkich kolumn |
| Szerokie tabele | Wsparcie dla maksymalnie 30 000 kolumn |
Note
Rzadkie kolumny to funkcja po stronie serwera. Sterownik mssql-python nie wymaga żadnej specjalnej konfiguracji ani API do pracy z rzadkimi kolumnami. Jedyna widoczna dla klienta różnica: gdy używasz zestawów kolumn, zwracają one reprezentację XML o rzadkich wartościach kolumn.
Najlepiej dopasowane do:
- Tabele z ponad 20–50% wartości NULL.
- Przechowywanie dokumentów z atrybutami zmiennymi.
- Wzorce EAV (Entity-Attribute-Value).
- Dane z czujników z wieloma opcjonalnymi odczytami.
Tworz rzadkie kolumny
Zdefiniuj rzadkie kolumny w schemacie tabeli, dodając SPARSE NULL modyfikator do kolumn, które często zawierają wartości NULL.
Podstawowa tabela kolumn rzadkich
Stwórz tabelę z rzadkimi kolumnami dla opcjonalnych atrybutów.
CREATE TABLE ProductAttributes (
ProductID INT PRIMARY KEY,
ProductName NVARCHAR(100) NOT NULL,
-- Sparse columns for optional attributes
Color NVARCHAR(50) SPARSE NULL,
Size NVARCHAR(20) SPARSE NULL,
Weight DECIMAL(10,2) SPARSE NULL,
Material NVARCHAR(100) SPARSE NULL,
Warranty INT SPARSE NULL,
Manufacturer NVARCHAR(100) SPARSE NULL
);
Z zestawem kolumn
Dodaj zestaw kolumn, aby zapewnić dostęp XML do wszystkich rzadkich kolumn jednocześnie.
CREATE TABLE ProductAttributesWithSet (
ProductID INT PRIMARY KEY,
ProductName NVARCHAR(100) NOT NULL,
-- Column set provides XML access to all sparse columns
SparseAttributes XML COLUMN_SET FOR ALL_SPARSE_COLUMNS,
-- Sparse columns
Color NVARCHAR(50) SPARSE NULL,
Size NVARCHAR(20) SPARSE NULL,
Weight DECIMAL(10,2) SPARSE NULL,
Material NVARCHAR(100) SPARSE NULL,
Warranty INT SPARSE NULL,
Manufacturer NVARCHAR(100) SPARSE NULL
);
Wstaw rzadkie dane kolumny
Wstawiaj do rzadkich kolumn według nazwy, tak jak w zwykłych kolumnach.
Wstaw poszczególne kolumny
Wstaw produkty z określonymi rzadkimi wartościami kolumn.
import mssql_python
conn = mssql_python.connect(connection_string)
cursor = conn.cursor()
# Create the table with sparse columns
cursor.execute("DROP TABLE IF EXISTS ProductAttributes")
cursor.execute("""
CREATE TABLE ProductAttributes (
ProductID INT PRIMARY KEY,
ProductName NVARCHAR(100) NOT NULL,
Color NVARCHAR(50) SPARSE NULL,
Size NVARCHAR(20) SPARSE NULL,
Weight DECIMAL(10,2) SPARSE NULL,
Material NVARCHAR(100) SPARSE NULL,
Warranty INT SPARSE NULL,
Manufacturer NVARCHAR(100) SPARSE NULL
)
""")
# Insert with some sparse columns populated
cursor.execute("""
INSERT INTO ProductAttributes (ProductID, ProductName, Color, Size)
VALUES (%(id)s, %(name)s, %(color)s, %(size)s)
""", {"id": 1, "name": "T-Shirt", "color": "Blue", "size": "Large"})
# Insert with different sparse columns
cursor.execute("""
INSERT INTO ProductAttributes (ProductID, ProductName, Weight, Material)
VALUES (%(id)s, %(name)s, %(weight)s, %(material)s)
""", {"id": 2, "name": "Coffee Mug", "weight": 0.35, "material": "Ceramic"})
conn.commit()
Wstaw za pomocą zestawu kolumn (XML)
Wstaw wiele rzadkich wartości kolumn jednocześnie, przekazując XML do zbioru kolumn.
# Create the table with a column set
cursor.execute("DROP TABLE IF EXISTS ProductAttributesWithSet")
cursor.execute("""
CREATE TABLE ProductAttributesWithSet (
ProductID INT PRIMARY KEY,
ProductName NVARCHAR(100) NOT NULL,
SparseAttributes XML COLUMN_SET FOR ALL_SPARSE_COLUMNS,
Color NVARCHAR(50) SPARSE NULL,
Size NVARCHAR(20) SPARSE NULL,
Weight DECIMAL(10,2) SPARSE NULL,
Material NVARCHAR(100) SPARSE NULL,
Warranty INT SPARSE NULL,
Manufacturer NVARCHAR(100) SPARSE NULL
)
""")
# Insert using column set XML
cursor.execute("""
INSERT INTO ProductAttributesWithSet (ProductID, ProductName, SparseAttributes)
VALUES (%(id)s, %(name)s, %(xml)s)
""", {
"id": 3,
"name": "Laptop Bag",
"xml": "<Color>Black</Color><Size>Medium</Size><Material>Nylon</Material><Warranty>24</Warranty>"
})
conn.commit()
Dynamiczne wstawianie atrybutów
Stwórz funkcję, która akceptuje dynamiczne atrybuty jako słownik i automatycznie buduje XML (XML).
def insert_with_attributes(cursor, product_id: int, name: str, attributes: dict):
"""Insert product with dynamic sparse column attributes."""
# Build XML for column set
xml_parts = [f"<{key}>{value}</{key}>" for key, value in attributes.items()]
attributes_xml = "".join(xml_parts)
cursor.execute("""
INSERT INTO ProductAttributesWithSet (ProductID, ProductName, SparseAttributes)
VALUES (%(id)s, %(name)s, %(xml)s)
""", {"id": product_id, "name": name, "xml": attributes_xml or None})
# Usage
insert_with_attributes(cursor, 4, "Headphones", {
"Color": "Silver",
"Warranty": 12,
"Manufacturer": "AudioTech"
})
conn.commit()
Wykonywanie zapytań dotyczących kolumn rozrzedzonych
Wykonuj zapytania dotyczące kolumn rzadkich po nazwie lub pobieraj jednocześnie wszystkie wartości kolumn rzadkich za pomocą zestawu kolumn.
Zapytuj poszczególne kolumny
Zapytaj konkretne rzadkie kolumny za pomocą standardowej składni SELECT.
# Query specific sparse columns
cursor.execute("""
SELECT ProductID, ProductName, Color, Size
FROM ProductAttributes
WHERE Color IS NOT NULL
""")
for row in cursor:
print(f"{row.ProductName}: {row.Color}, {row.Size}")
Zapytanie przez zbiór kolumn
Pobierz wszystkie rzadkie wartości kolumn jako XML ze zbioru kolumn.
# Get column set XML
cursor.execute("""
SELECT ProductID, ProductName, SparseAttributes
FROM ProductAttributesWithSet
WHERE ProductID = %(id)s
""", {"id": 3})
row = cursor.fetchone()
print(f"Product: {row.ProductName}")
print(f"Attributes XML: {row.SparseAttributes}")
Analizować kod XML zestawu kolumn w Pythonie
Rozparuj XML z zestawu kolumn, aby przekonwertować go na słownik Python dla łatwiejszej manipulacji.
from xml.etree import ElementTree as ET
def get_product_attributes(cursor, product_id: int) -> dict:
"""Get product with parsed sparse attributes."""
cursor.execute("""
SELECT ProductName, SparseAttributes
FROM ProductAttributesWithSet
WHERE ProductID = %(id)s
""", {"id": product_id})
row = cursor.fetchone()
if row is None:
return None
result = {"ProductName": row.ProductName}
# Parse XML column set
if row.SparseAttributes:
# Wrap in root element for parsing
xml_str = f"<root>{row.SparseAttributes}</root>"
root = ET.fromstring(xml_str)
for elem in root:
result[elem.tag] = elem.text
return result
# Usage
product = get_product_attributes(cursor, 3)
print(product)
# {'ProductName': 'Laptop Bag', 'Color': 'Black', 'Size': 'Medium', 'Material': 'Nylon', 'Warranty': '24'}
Zapytanie za pomocą SELECT *
Gdy używasz SELECT *, zestaw kolumn zwraca jako pojedynczą kolumnę XML zamiast pojedynczych kolumn rzadkich.
# SELECT * returns column set instead of individual sparse columns
cursor.execute("""
SELECT * FROM ProductAttributesWithSet WHERE ProductID = %(id)s
""", {"id": 3})
row = cursor.fetchone()
# Returns: ProductID, ProductName, SparseAttributes (not individual columns)
print(f"Columns: {[col[0] for col in cursor.description]}")
Jawnie odwołuj się do poszczególnych kolumn
Aby pobrać pojedyncze rzadkie kolumny z tabeli z zestawem kolumn, wymień je jawnie w klauzuli SELECT.
# To get individual sparse columns with column set table, list them explicitly
cursor.execute("""
SELECT ProductID, ProductName, Color, Size, Weight, Material, Warranty, Manufacturer
FROM ProductAttributesWithSet
WHERE ProductID = %(id)s
""", {"id": 3})
# Now each sparse column is available as separate property
row = cursor.fetchone()
print(f"Color: {row.Color}, Material: {row.Material}")
Aktualizuj rzadkie kolumny
Aktualizuj poszczególne kolumny rzadkie według nazwy lub zastąp wszystkie rzadkie wartości jednocześnie za pomocą zestawu kolumn XML.
Aktualizuj poszczególne kolumny
Aktualizuj konkretne rzadkie wartości kolumn za pomocą standardowej składni SQL UPDATE .
cursor.execute("""
UPDATE ProductAttributes
SET Color = %(color)s, Weight = %(weight)s
WHERE ProductID = %(id)s
""", {"id": 1, "color": "Red", "weight": 0.2})
conn.commit()
Aktualizacja za pomocą zestawu kolumn
Zastąp wszystkie wartości w kolumnach SPARSE, bezpośrednio aktualizując kod XML zestawu kolumn.
# Replace all sparse column values via column set
cursor.execute("""
UPDATE ProductAttributesWithSet
SET SparseAttributes = %(xml)s
WHERE ProductID = %(id)s
""", {
"id": 3,
"xml": "<Color>Navy</Color><Size>Large</Size><Material>Leather</Material>"
})
conn.commit()
# Note: This clears any sparse columns not included in the XML
Częściowa aktualizacja przez zestaw kolumn
Aktualizuj tylko konkretne rzadkie atrybuty, zachowując wartości innych atrybutów, które nie są uwzględnione w aktualizacji.
# To update only specific attributes, merge with existing
def update_attributes(cursor, product_id: int, updates: dict):
"""Update specific sparse attributes while preserving others."""
# Get current attributes
cursor.execute("""
SELECT Color, Size, Weight, Material, Warranty, Manufacturer
FROM ProductAttributesWithSet
WHERE ProductID = %(id)s
""", {"id": product_id})
row = cursor.fetchone()
if row is None:
raise ValueError(f"Product {product_id} not found")
# Merge updates
current = {
"Color": row.Color,
"Size": row.Size,
"Weight": row.Weight,
"Material": row.Material,
"Warranty": row.Warranty,
"Manufacturer": row.Manufacturer
}
for key, value in updates.items():
current[key] = value
# Build XML with non-null values
xml_parts = []
for key, value in current.items():
if value is not None:
xml_parts.append(f"<{key}>{value}</{key}>")
cursor.execute("""
UPDATE ProductAttributesWithSet
SET SparseAttributes = %(xml)s
WHERE ProductID = %(id)s
""", {"id": product_id, "xml": "".join(xml_parts) or None})
# Usage
update_attributes(cursor, 3, {"Color": "Brown", "Warranty": 36})
conn.commit()
Dynamiczne wzory kolumnowe
Buduj elastyczne zapytania dynamicznie przeszukujące rzadkie kolumny, wykorzystując listy dozwolonych do weryfikacji nazw kolumn i zapobiegania atakom SQL injection.
Zapytanie o dane w stylu EAV
Wyszukaj produkty według określonej nazwy atrybutu i wartości, używając dopasowywania wzorca Entity-Attribute-Value (EAV).
def find_products_by_attribute(cursor, attribute_name: str, attribute_value: str) -> list:
"""Find products with specific attribute value."""
# Validate column name against allowed sparse columns to prevent SQL injection
allowed_columns = {"Color", "Size", "Weight", "Material", "Warranty", "Manufacturer"}
if attribute_name not in allowed_columns:
raise ValueError(f"Invalid attribute: {attribute_name}")
cursor.execute(f"""
SELECT ProductID, ProductName, {attribute_name}
FROM ProductAttributesWithSet
WHERE {attribute_name} = %(value)s
""", {"value": attribute_value})
return cursor.fetchall()
# Find all blue products
blue_products = find_products_by_attribute(cursor, "Color", "Blue")
Elastyczne wyszukiwanie atrybutów
Szukaj produktów spełniających jednocześnie wiele opcjonalnych atrybutów.
def search_by_attributes(cursor, **attributes) -> list:
"""Search products by multiple optional attributes."""
# Validate column names against allowed sparse columns to prevent SQL injection
allowed_columns = {"Color", "Size", "Weight", "Material", "Warranty", "Manufacturer"}
invalid = set(attributes.keys()) - allowed_columns
if invalid:
raise ValueError(f"Invalid attributes: {invalid}")
conditions = ["1=1"] # Always true base condition
params = {}
for i, (key, value) in enumerate(attributes.items()):
if value is not None:
conditions.append(f"{key} = %(attr_{i})s")
params[f"attr_{i}"] = value
query = f"""
SELECT ProductID, ProductName, SparseAttributes
FROM ProductAttributesWithSet
WHERE {' AND '.join(conditions)}
"""
cursor.execute(query, params)
return cursor.fetchall()
# Search by multiple attributes
results = search_by_attributes(cursor, Color="Black", Material="Nylon")
Zagadnienia dotyczące wydajności
Oceń, kiedy rzadkie kolumny przynoszą korzyści z magazynowania, oceń koszty ogólne i wykorzystaj monitorowanie wydajności, aby zoptymalizować projekt kolumn rzadkich.
Kiedy używać rzadkich kolumn
Dobrymi kandydatami dla kolumn rzadkich są:
- Kolumny, w których ponad 60–70% wartości to NULL.
- Szerokie tabele z wieloma opcjonalnymi kolumnami.
- Obciążenia, gdzie priorytetem jest optymalizacja pamięci masowej.
Unikaj używania rzadkich kolumn, gdy:
- Większość wierszy zawiera wartości (każda wartość różna od NULL dodaje 4 bajty narzutu).
- Kolumna jest często używana w klauzulach WHERE.
- Kolumna jest częścią indeksu skupionego (clustered index).
Sprawdź oszczędności miejsca na dysku
Porównaj rozmiar zajmowanego miejsca przez rzadką i nierzadką wersję tej samej tabeli.
-- Compare storage with and without sparse
EXEC sp_spaceused 'ProductAttributes';
EXEC sp_spaceused 'ProductAttributesWithoutSparse';
Rozważania dotyczące indeksu
Możesz indeksować rzadkie kolumny. Indeksy filtrowane dobrze sprawdzają się przy danych rzadkich, ponieważ pomijają wiersze NULL:
CREATE INDEX IX_Products_Color
ON ProductAttributes(Color)
WHERE Color IS NOT NULL;
Operacje zbiorcze
Zoptymalizuj operacje wstawiania wielu produktów z użyciem kolumn rzadkich, tworząc kod XML zestawu kolumn w języku Python przed przekazaniem wierszy do elementu bulkcopy().
Wkład zbiorczy z rzadkimi kolumnami
Użyj kopii masowej, aby efektywnie wstawiać wiele produktów o rzadkich atrybutach.
def bulk_insert_with_attributes(conn, products: list[dict]):
"""Bulk insert products with sparse attributes."""
rows = []
for product in products:
attrs = product.get("attributes", {})
xml = "".join(f"<{k}>{v}</{k}>" for k, v in attrs.items()) or None
rows.append((product["id"], product["name"], xml))
cursor = conn.cursor()
result = cursor.bulkcopy("ProductAttributesWithSet", rows)
return result["rows_copied"]
# Usage
products = [
{"id": 100, "name": "Widget A", "attributes": {"Color": "Red", "Size": "Small"}},
{"id": 101, "name": "Widget B", "attributes": {"Weight": 1.5, "Material": "Steel"}},
{"id": 102, "name": "Widget C", "attributes": {}}, # No sparse attributes
]
bulk_insert_with_attributes(conn, products)
conn.commit()
Najlepsze rozwiązania
Stosuj wzorce walidacji, korzystaj z zestawów kolumn dla elastyczności i monitoruj rzadkość kolumn, aby upewnić się, że Twój projekt rzadkich kolumn spełnia cele wydajności i utrzymalności.
Zweryfikowaj rzadkie wartości kolumnowe
Sprawdź, czy wszystkie rzadkie atrybuty odpowiadają dozwolonemu zbiorowi, zanim włożysz dane.
# Sparse columns have the same constraints as regular columns
# The SPARSE keyword only affects storage
def validate_and_insert(cursor, product_id: int, name: str, attributes: dict):
"""Insert with validation."""
allowed_attributes = {"Color", "Size", "Weight", "Material", "Warranty", "Manufacturer"}
invalid = set(attributes.keys()) - allowed_attributes
if invalid:
raise ValueError(f"Unknown attributes: {invalid}")
xml_parts = [f"<{k}>{v}</{k}>" for k, v in attributes.items()]
cursor.execute("""
INSERT INTO ProductAttributesWithSet (ProductID, ProductName, SparseAttributes)
VALUES (%(id)s, %(name)s, %(xml)s)
""", {"id": product_id, "name": name, "xml": "".join(xml_parts) or None})
Używaj zestawów kolumn dla elastyczności
Zestawy kolumn upraszczają pracę z rzadkimi kolumnami:
- Dodaj nowe rzadkie kolumny bez zmian w kodzie.
- Przechowuj atrybuty dynamiczne.
- Automatycznie serializuj i deserializuj XML.
Bez zestawu kolumn potrzebujesz wyraźnych list kolumn. W przypadku zestawu kolumn, kolumna XML automatycznie obsługuje atrybuty dynamiczne.
Monitorowanie wartości procentowych NULL
Przeanalizuj odsetek wartości NULL w kolumnie, aby określić, czy jest ona dobrym kandydatem do optymalizacji kolumn rzadkich.
def analyze_sparseness(cursor, table: str, column: str) -> float:
"""Check if column is a good sparse candidate."""
# Validate identifiers to prevent SQL injection
import re
if not re.match(r'^[A-Za-z_][A-Za-z0-9_.]*$', table):
raise ValueError(f"Invalid table name: {table}")
if not re.match(r'^[A-Za-z_][A-Za-z0-9_]*$', column):
raise ValueError(f"Invalid column name: {column}")
cursor.execute(f"""
SELECT
COUNT(*) AS TotalRows,
SUM(CASE WHEN {column} IS NULL THEN 1 ELSE 0 END) AS NullRows
FROM {table}
""")
row = cursor.fetchone()
null_percentage = (row.NullRows / row.TotalRows * 100) if row.TotalRows > 0 else 0
print(f"Column {column}: {null_percentage:.1f}% NULL")
print(f"Recommendation: {'Good sparse candidate' if null_percentage > 60 else 'Keep as regular column'}")
return null_percentage