Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Microsoft SQL 2016 et versions ultérieures ainsi que Azure SQL fournissent un support JSON via des fonctions qui fonctionnent sur nvarchar des colonnes. Le pilote mssql-python envoie et reçoit du JSON sous forme de chaînes Python classiques. Vous pouvez:
- Stockez le JSON sous forme de chaînes dans des colonnes
nvarchar. - Requête en JSON avec des expressions de chemin en utilisant
JSON_VALUE,JSON_QUERY, etOPENJSON. - Transformer les données relationnelles en JSON avec
FOR JSON. - Analysez le JSON en format relationnel avec
OPENJSON.
Note
Microsoft SQL stocke les données JSON en nvarchar colonnes, et non dans un type de colonne JSON dédié. Le pilote mssql-python envoie et reçoit du JSON sous forme de chaînes régulières. Utilisez le module intégré json de Python pour sérialiser et désérialiser côté client.
Stocker les données JSON
Sérialisez les dictats Python vers des chaînes avec json.dumps() avant de les insérer dans les colonnes nvarchar.
Insertion de chaîne JSON
Stockez un dictionnaire Python sous forme de texte JSON dans une table de base de données :
import json
import mssql_python
conn = mssql_python.connect(
"Server=<server>.database.windows.net;"
"Database=<database>;"
"Authentication=ActiveDirectoryDefault;"
"Encrypt=yes"
)
cursor = conn.cursor()
# Create table with JSON column
cursor.execute("""
CREATE TABLE #JsonProducts (
ProductID INT IDENTITY PRIMARY KEY,
Name NVARCHAR(100),
JsonData NVARCHAR(MAX)
)
""")
# Python dict to JSON string
product_data = {
"name": "Widget Pro",
"specs": {
"weight": 2.5,
"dimensions": {"width": 10, "height": 5, "depth": 3}
},
"tags": ["electronics", "gadgets", "bestseller"]
}
cursor.execute("""
INSERT INTO #JsonProducts (Name, JsonData)
VALUES (%(name)s, %(json)s)
""", {"name": "Widget Pro", "json": json.dumps(product_data)})
conn.commit()
Valider le JSON lors de l’insertion
Utilisez la ISJSON() fonction pour valider la syntaxe JSON avant d’insérer :
data = {"name": "Widget", "specs": {"weight": 1.5}}
cursor.execute("""
CREATE TABLE #JsonValidate (
ProductID INT IDENTITY, Name NVARCHAR(100), JsonData NVARCHAR(MAX)
)
""")
cursor.execute("""
INSERT INTO #JsonValidate (Name, JsonData)
SELECT %(name)s, %(json)s
WHERE ISJSON(%(json)s) = 1
""", {"name": "Widget", "json": json.dumps(data)})
if cursor.rowcount == 0:
raise ValueError("Invalid JSON data")
Interroger des données JSON
Utilisez les fonctions de chemin JSON SQL Microsoft pour extraire les valeurs sur le serveur avant de les renvoyer au client.
Extraire les valeurs scalaires
À utiliser JSON_VALUE pour extraire des valeurs uniques :
# Create table with sample JSON data
cursor.execute("""
CREATE TABLE #JsonExtract (
ProductID INT IDENTITY, Name NVARCHAR(100), JsonData NVARCHAR(MAX)
)
""")
cursor.execute("""
INSERT INTO #JsonExtract (Name, JsonData) VALUES (
'Widget Pro',
'{"name":"Widget Pro","specs":{"weight":2.5,"dimensions":{"width":10,"height":5,"depth":3}},"tags":["electronics","gadgets"]}'
)
""")
cursor.execute("""
SELECT
Name,
JSON_VALUE(JsonData, '$.specs.weight') AS Weight,
JSON_VALUE(JsonData, '$.specs.dimensions.width') AS Width
FROM #JsonExtract
WHERE JSON_VALUE(JsonData, '$.name') = %(name)s
""", {"name": "Widget Pro"})
row = cursor.fetchone()
print(f"Weight: {row.Weight}, Width: {row.Width}")
Extraire des objets ou des tableaux
À utiliser JSON_QUERY pour les objets et les tableaux.
cursor.execute("""
CREATE TABLE #JsonQuery (
ProductID INT IDENTITY, Name NVARCHAR(100), JsonData NVARCHAR(MAX)
)
""")
cursor.execute("""
INSERT INTO #JsonQuery (Name, JsonData) VALUES (
'Widget Pro',
'{"specs":{"weight":2.5,"color":"blue"},"tags":["electronics","gadgets"]}'
)
""")
cursor.execute("""
SELECT
Name,
JSON_QUERY(JsonData, '$.specs') AS Specs,
JSON_QUERY(JsonData, '$.tags') AS Tags
FROM #JsonQuery
""")
for row in cursor:
specs = json.loads(row.Specs) if row.Specs else {}
tags = json.loads(row.Tags) if row.Tags else []
print(f"{row.Name}: {specs}, Tags: {tags}")
Convertir un tableau JSON en lignes
Décomposez un tableau JSON en lignes en utilisant OPENJSON:
cursor.execute("""
CREATE TABLE #JsonArray (
ProductID INT IDENTITY, Name NVARCHAR(100), JsonData NVARCHAR(MAX)
)
""")
cursor.execute("""
INSERT INTO #JsonArray (Name, JsonData) VALUES
('Widget Pro', '{"tags":["electronics","gadgets","bestseller"]}'),
('Gadget X', '{"tags":["tools","gadgets"]}')
""")
cursor.execute("""
SELECT p.Name, t.value AS Tag
FROM #JsonArray p
CROSS APPLY OPENJSON(p.JsonData, '$.tags') t
""")
for row in cursor:
print(f"Product: {row.Name}, Tag: {row.Tag}")
Analyser l’objet JSON en colonnes
Extraire des champs individuels des objets JSON en utilisant JSON_VALUE() et lancer les résultats vers des types SQL appropriés.
cursor.execute("""
CREATE TABLE #JsonCols (
ProductID INT IDENTITY, Name NVARCHAR(100), JsonData NVARCHAR(MAX)
)
""")
cursor.execute("""
INSERT INTO #JsonCols (Name, JsonData) VALUES (
'Widget Pro',
'{"name":"Widget Pro","specs":{"weight":2.5,"dimensions":{"width":10,"height":5}}}'
)
""")
cursor.execute("""
SELECT
p.ProductID,
j.name AS ProductName,
j.weight,
j.width,
j.height
FROM #JsonCols p
CROSS APPLY OPENJSON(p.JsonData)
WITH (
name NVARCHAR(100) '$.name',
weight DECIMAL(5,2) '$.specs.weight',
width INT '$.specs.dimensions.width',
height INT '$.specs.dimensions.height'
) j
""")
Modifier les données JSON
À utiliser JSON_MODIFY pour mettre à jour un chemin spécifique dans un document JSON sans réécrire toute la valeur.
Mettre à jour la valeur JSON
Modifier une seule propriété JSON en utilisant JSON_MODIFY:
cursor.execute("""
CREATE TABLE #JsonMod (
ProductID INT IDENTITY, Name NVARCHAR(100), JsonData NVARCHAR(MAX)
)
""")
cursor.execute("""
INSERT INTO #JsonMod (Name, JsonData) VALUES (
'Widget Pro',
'{"specs":{"weight":2.5,"dimensions":{"width":10}},"tags":["electronics"]}'
)
""")
cursor.execute("""
UPDATE #JsonMod
SET JsonData = JSON_MODIFY(JsonData, '$.specs.weight', %(weight)s)
WHERE ProductID = %(id)s
""", {"weight": 3.0, "id": 1})
conn.commit()
Ajouter la propriété JSON
Insérer une nouvelle propriété dans un objet JSON existant :
cursor.execute("""
CREATE TABLE #JsonAdd (
ProductID INT IDENTITY, Name NVARCHAR(100), JsonData NVARCHAR(MAX)
)
""")
cursor.execute("""
INSERT INTO #JsonAdd (Name, JsonData) VALUES (
'Widget Pro', '{"specs":{"weight":2.5}}'
)
""")
cursor.execute("""
UPDATE #JsonAdd
SET JsonData = JSON_MODIFY(JsonData, '$.specs.color', %(color)s)
WHERE ProductID = %(id)s
""", {"color": "blue", "id": 1})
Supprimer la propriété JSON
Supprimez une propriété d’un objet JSON en la définissant à NULL:
cursor.execute("""
CREATE TABLE #JsonRem (
ProductID INT IDENTITY, Name NVARCHAR(100), JsonData NVARCHAR(MAX)
)
""")
cursor.execute("""
INSERT INTO #JsonRem (Name, JsonData) VALUES (
'Widget Pro', '{"specs":{"weight":2.5,"color":"blue"}}'
)
""")
cursor.execute("""
UPDATE #JsonRem
SET JsonData = JSON_MODIFY(JsonData, '$.specs.color', NULL)
WHERE ProductID = %(id)s
""", {"id": 1})
Ajouter au tableau JSON
Ajoutez une nouvelle valeur à la fin d’un tableau JSON en utilisant append une directive dans JSON_MODIFY:
cursor.execute("""
CREATE TABLE #JsonAppend (
ProductID INT IDENTITY, Name NVARCHAR(100), JsonData NVARCHAR(MAX)
)
""")
cursor.execute("""
INSERT INTO #JsonAppend (Name, JsonData) VALUES (
'Widget Pro', '{"tags":["electronics","gadgets"]}'
)
""")
cursor.execute("""
UPDATE #JsonAppend
SET JsonData = JSON_MODIFY(
JsonData,
'append $.tags',
%(tag)s
)
WHERE ProductID = %(id)s
""", {"tag": "new-arrival", "id": 1})
Convertir des données relationnelles en JSON
La FOR JSON clause transforme les résultats de requête en une chaîne JSON côté serveur.
POUR JSON AUTO
Générer du JSON à partir des résultats de requête :
cursor.execute("""
SELECT TOP 5 o.SalesOrderID, p.LastName AS CustomerName, o.TotalDue
FROM Sales.SalesOrderHeader o
JOIN Sales.Customer c ON o.CustomerID = c.CustomerID
JOIN Person.Person p ON c.PersonID = p.BusinessEntityID
FOR JSON AUTO
""")
# Result is a single string containing JSON
json_result = cursor.fetchval()
orders = json.loads(json_result)
print(json.dumps(orders, indent=2))
FOR JSON PATH
Obtenez plus de contrôle sur la structure JSON :
cursor.execute("""
SELECT
o.SalesOrderID AS 'order.id',
o.OrderDate AS 'order.date',
p.LastName AS 'customer.name',
e.EmailAddress AS 'customer.email'
FROM Sales.SalesOrderHeader o
JOIN Sales.Customer c ON o.CustomerID = c.CustomerID
JOIN Person.Person p ON c.PersonID = p.BusinessEntityID
JOIN Person.EmailAddress e ON p.BusinessEntityID = e.BusinessEntityID
WHERE o.SalesOrderID = %(id)s
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
""", {"id": 43659})
json_result = cursor.fetchval()
order = json.loads(json_result)
# Structure: {"order": {"id": 43659, "date": "..."}, "customer": {"name": "...", "email": "..."}}
JSON imbriqué
Interroger des structures de données contenant des tableaux et des objets JSON imbriqués en utilisant des sous-requêtes avec FOR JSON pour construire une sortie JSON hiérarchique.
cursor.execute("""
SELECT TOP 3
c.CustomerID,
p.LastName AS CustomerName,
(SELECT TOP 3 o.SalesOrderID, o.TotalDue
FROM Sales.SalesOrderHeader o
WHERE o.CustomerID = c.CustomerID
FOR JSON PATH) AS Orders
FROM Sales.Customer c
JOIN Person.Person p ON c.PersonID = p.BusinessEntityID
WHERE c.PersonID IS NOT NULL
FOR JSON PATH
""")
json_result = cursor.fetchval()
customers = json.loads(json_result)
# Each customer has nested Orders array
Modèles d’intégration Python
Ces schémas montrent comment construire des abstractions Python sur des tables soutenues par JSON.
Modèle de dépôt avec JSON
Implémentez une couche d’accès aux données qui sérialise et désériase les objets Python en colonnes JSON, fournissant une interface sûre pour les types à la base de données.
from dataclasses import dataclass, asdict
from typing import Optional
import json
@dataclass
class ProductSpecs:
weight: float
color: str
dimensions: dict
@dataclass
class Product:
id: Optional[int]
name: str
specs: ProductSpecs
class ProductRepository:
def __init__(self, connection):
self.conn = connection
cursor = self.conn.cursor()
cursor.execute("""
IF OBJECT_ID('#JsonRepo') IS NULL
CREATE TABLE #JsonRepo (
ProductID INT IDENTITY PRIMARY KEY,
Name NVARCHAR(100),
JsonData NVARCHAR(MAX)
)
""")
self.conn.commit()
def save(self, product: Product) -> int:
cursor = self.conn.cursor()
specs_json = json.dumps(asdict(product.specs))
if product.id:
cursor.execute("""
UPDATE #JsonRepo SET Name = %(name)s, JsonData = %(json)s
WHERE ProductID = %(id)s
""", {"name": product.name, "json": specs_json, "id": product.id})
else:
cursor.execute("""
INSERT INTO #JsonRepo (Name, JsonData)
OUTPUT INSERTED.ProductID
VALUES (%(name)s, %(json)s)
""", {"name": product.name, "json": specs_json})
product.id = cursor.fetchval()
self.conn.commit()
return product.id
def get(self, product_id: int) -> Optional[Product]:
cursor = self.conn.cursor()
cursor.execute("""
SELECT ProductID, Name, JsonData FROM #JsonRepo WHERE ProductID = %(id)s
""", {"id": product_id})
row = cursor.fetchone()
if row is None:
return None
specs_data = json.loads(row.JsonData)
return Product(
id=row.ProductID,
name=row.Name,
specs=ProductSpecs(**specs_data)
)
Connectez-vous à la base de données, puis créez le dépôt et utilisez-le pour sauvegarder et récupérer un produit. La méthode save() emprunte la branche INSERT lorsque id est None, et la branche UPDATE sinon :
conn = mssql_python.connect(connection_string)
repo = ProductRepository(conn)
# id is None, so save() inserts a new row and returns the generated ProductID.
product = Product(
id=None,
name="Widget Pro",
specs=ProductSpecs(weight=2.5, color="black", dimensions={"width": 10, "height": 5})
)
product_id = repo.save(product)
print(f"Saved product {product_id}")
# Read the product back into a typed Product object.
loaded = repo.get(product_id)
print(loaded)
conn.close()
Le dépôt crée #JsonRepo comme table temporaire locale, limitée à la connexion que vous transmettez ; save() et get() doivent donc partager cette même connexion. La table est supprimée lorsque la connexion est fermée.
Gérer efficacement les gros résultats JSON
Lorsque les résultats JSON sont volumineux, récupérez-les par morceaux sur plusieurs lignes.
def fetch_json_in_parts(cursor, query: str, params: dict) -> list:
"""Handle JSON results that might span multiple rows."""
cursor.execute(query, params)
# FOR JSON might split large results across rows
json_parts = []
for row in cursor:
json_parts.append(row[0])
# Combine parts
json_string = "".join(json_parts)
return json.loads(json_string) if json_string else []
# Usage
data = fetch_json_in_parts(cursor, "SELECT TOP 100 * FROM Production.Product FOR JSON AUTO", {})
Convertir les résultats de requêtes en JSON en Python
Transformez les résultats des requêtes relationnelles en format JSON en Python en convertissant chaque ligne en dictionnaire, puis sérialisez en JSON.
def query_to_json(cursor, query: str, params: dict = None) -> str:
"""Execute query and return results as JSON string."""
cursor.execute(query, params or {})
columns = [col[0] for col in cursor.description]
rows = []
for row in cursor:
rows.append(dict(zip(columns, row)))
return json.dumps(rows, default=str, indent=2)
# Usage
json_output = query_to_json(cursor, "SELECT TOP 5 ProductID, Name, ListPrice FROM Production.Product WHERE ProductSubcategoryID = %(cat)s", {"cat": 1})
print(json_output)
Indexer des données JSON
Créez une colonne calculée soutenue par une expression de chemin JSON pour rendre le chemin indexable.
Colonne calculée avec index
Définissez une colonne calculée qui extrait une valeur JSON et y appliquez un index pour un filtrage efficace sur les chemins JSON fréquemment interrogés. L’exemple suivant crée une table permanente, ajoute une colonne calculée persistante sur le chemin JSON $.specs.weight , et crée un index dessus.
cursor.execute("""
IF OBJECT_ID('dbo.ProductCatalog', 'U') IS NOT NULL
DROP TABLE dbo.ProductCatalog
""")
cursor.execute("""
CREATE TABLE dbo.ProductCatalog (
ProductID INT IDENTITY PRIMARY KEY,
Name NVARCHAR(100),
JsonData NVARCHAR(MAX)
)
""")
# Insert sample rows with JSON data
rows = [
("Widget Pro", '{"specs":{"weight":2.5,"color":"blue"}}'),
("Gadget X", '{"specs":{"weight":0.8,"color":"red"}}'),
("Heavy Duty", '{"specs":{"weight":9.1,"color":"gray"}}'),
]
cursor.executemany(
"INSERT INTO dbo.ProductCatalog (Name, JsonData) VALUES (%(name)s, %(json)s)",
[{"name": n, "json": j} for n, j in rows]
)
conn.commit()
# Add a persisted computed column that extracts weight from JSON
cursor.execute("""
ALTER TABLE dbo.ProductCatalog
ADD ProductWeight AS CAST(JSON_VALUE(JsonData, '$.specs.weight') AS DECIMAL(5,2)) PERSISTED
""")
# Index the computed column for efficient range queries
cursor.execute("""
CREATE INDEX IX_ProductCatalog_Weight
ON dbo.ProductCatalog (ProductWeight)
""")
conn.commit()
Requête utilisant une colonne calculée indexée
Filtrez directement par la colonne calculée. Le moteur de requête utilise l’index au lieu de scanner et analyser chaque document JSON.
cursor.execute("""
SELECT Name, ProductWeight
FROM dbo.ProductCatalog
WHERE ProductWeight > %(min_weight)s
ORDER BY ProductWeight
""", {"min_weight": 1.0})
for row in cursor:
print(f"{row.Name}: {row.ProductWeight} kg")
# Cleanup
cursor.execute("DROP TABLE dbo.ProductCatalog")
conn.commit()
Choisissez entre colonnes relationnelles et stockage JSON
Utilisez des colonnes relationnelles lorsque les données ont un schéma fixe, nécessitent une intégrité référentielle, participent aux JOINs ou apparaissent fréquemment dans les clauses WHERE. Utilisez des colonnes JSON (nvarchar(max)) lorsque les données sont clairsemées, varient selon les lignes ou représentent une configuration ou des métadonnées flexibles.
Quand utiliser le traitement JSON côté serveur versus côté client
Utilisez les fonctions JSON Microsoft SQL (JSON_VALUE, JSON_QUERY, OPENJSON) lorsque vous devez filtrer, indexer ou agréger entre les champs JSON sans récupérer chaque ligne vers le client. Ce choix est correct lorsque seul un sous-ensemble de lignes correspond à vos critères, ou lorsque vous souhaitez calculer des indices de colonnes sur des chemins JSON.
Utilisez le traitement Python côté client (json.loads()) lorsque vous récupérez des documents entiers et les traitez en logique applicative. Cette approche fonctionne bien quand vous avez besoin du document complet et que vous ne filtrez pas sur les champs JSON dans la base de données.
Flux de travail de type document
Lorsque votre application stocke et récupère des documents entiers, utilisez la sérialisation côté Python et traitez la colonne JSON comme un stockage opaque. Traitez et interrogez les documents en Python en récupérant et en désérialisant des blobs JSON complets :
import json
# Create the settings table
cursor.execute("""
CREATE TABLE #Settings (
UserID INT PRIMARY KEY,
ConfigJson NVARCHAR(MAX)
)
""")
# Store a configuration document
config = {
"theme": "dark",
"notifications": {"email": True, "sms": False},
"custom_fields": {"department": "Engineering", "cost_center": "CC-100"}
}
cursor.execute(
"INSERT INTO #Settings (UserID, ConfigJson) VALUES (%(uid)s, %(cfg)s)",
{"uid": 1, "cfg": json.dumps(config)}
)
# Retrieve and process in Python
cursor.execute("SELECT ConfigJson FROM #Settings WHERE UserID = %(uid)s", {"uid": 1})
row = cursor.fetchone()
config = json.loads(row.ConfigJson)
print(config["notifications"]["email"]) # True
Requêtes JSON côté serveur
Utilisez des fonctions JSON Microsoft SQL lorsque vous devez filtrer, indexer ou agréger entre les champs JSON sans récupérer chaque ligne. Cette approche est plus efficace que de charger toutes les lignes dans Python pour filtrer la mémoire :
-
JSON_VALUEExtrait des valeurs scalaires et peut recalculer les indices de colonnes. -
JSON_QUERYextrait des objets et des tableaux. -
OPENJSONdécompose le JSON en lignes pourJOINs et l’agrégation. -
JSON_MODIFYmet à jour des chemins spécifiques sans réécrire tout le document.
# Filter by a JSON field server-side
cursor.execute("""
SELECT UserID, ConfigJson
FROM #Settings
WHERE JSON_VALUE(ConfigJson, '$.custom_fields.department') = %(dept)s
""", {"dept": "Engineering"})
Pour les chemins JSON fréquemment interrogés, créez une colonne calculée avec un index :
ALTER TABLE Settings
ADD Department AS JSON_VALUE(ConfigJson, '$.custom_fields.department');
CREATE INDEX IX_Settings_Department ON Settings(Department);
Bonnes pratiques
Appliquez ces directives pour utiliser les colonnes JSON de manière fiable.
Valider JSON avant stockage
Validez le JSON et les identifiants de table/colonne avant de le stocker pour éviter les attaques d’injection.
def store_json_safely(cursor, table: str, json_column: str, data: dict):
"""Store JSON with validation."""
# 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_]*$', json_column):
raise ValueError(f"Invalid column name: {json_column}")
json_str = json.dumps(data)
# Check if valid JSON in Microsoft SQL
cursor.execute("SELECT ISJSON(%(json)s)", {"json": json_str})
if cursor.fetchval() != 1:
raise ValueError("Invalid JSON")
cursor.execute(f"INSERT INTO {table} ({json_column}) VALUES (%(json)s)", {"json": json_str})
N’utilisez pas trop le JSON
Utilisez des colonnes JSON pour des données flexibles ou clairsemées, comme les préférences de l’utilisateur ou des champs personnalisés. Utilisez des colonnes relationnelles pour :
- Données fréquemment interrogées.
- Des données qui nécessitent une intégrité référentielle.
- Colonnes utilisées dans les clauses
WHERE.
Gérer None/NULL correctement
Gérez les champs JSON manquants ou optionnels en insérant des valeurs NULL pour les colonnes qui ne contiennent pas de données.
cursor.execute("""
CREATE TABLE #JsonOpt (
ProductID INT IDENTITY, Name NVARCHAR(100), JsonData NVARCHAR(MAX)
)
""")
cursor.execute("""
INSERT INTO #JsonOpt (Name, JsonData) VALUES (
'Widget Pro', '{"required_field":"value"}'
)
""")
cursor.execute("""
SELECT
Name,
JSON_VALUE(JsonData, '$.optional_field') AS OptionalValue
FROM #JsonOpt
""")
for row in cursor:
# JSON_VALUE returns NULL if path doesn't exist
value = row.OptionalValue or "default"
print(f"{row.Name}: {value}")