Zpracování dat JSON pomocí předdefinovaných funkcí
Návod
Další podrobnosti najdete na kartě Text a obrázky .
Představte si scénář, ve kterém vaše aplikace elektronického obchodování ukládá předvolby zákazníků a metadata objednávek jako dokumenty JSON. Mobilní aplikace odesílá data nákupního košíku ve formátu JSON a váš systém generování sestav musí exportovat katalogy produktů jako JSON pro webové rozhraní API. Práce přímo s JSON ve vaší databázi eliminuje potřebu transformací aplikační vrstvy a zajišťuje efektivní zpracování dat.
Databáze SQL Server, Azure SQL a SQL ve službě Fabric poskytují integrovanou podporu JSON, která umožňuje analyzovat, dotazovat, vytvářet a transformovat data JSON přímo v T-SQL. V této lekci se dozvíte, jak pomocí funkcí JSON extrahovat hodnoty, vytvořit výstup JSON, agregovat data do polí JSON a ověřit obsah JSON.
Extrahování hodnot pomocí JSON_VALUE a JSON_QUERY
Při práci s JSON uloženými v databázi musíte extrahovat konkrétní hodnoty pro filtrování, spojování nebo zobrazení. SQL Server poskytuje pro tento účel dvě funkce:
JSON_VALUE() extrahuje skalární hodnotu (řetězec, číslo, logická hodnota) z řetězce JSON:
DECLARE @json NVARCHAR(MAX) = N'{
"customer": {
"id": 12345,
"name": "Contoso Ltd",
"active": true
},
"orderTotal": 1599.99
}';
SELECT
JSON_VALUE(@json, '$.customer.id') AS CustomerID,
JSON_VALUE(@json, '$.customer.name') AS CustomerName,
JSON_VALUE(@json, '$.orderTotal') AS OrderTotal;
Výsledek bude:
CustomerID CustomerName OrderTotal
---------- ------------ ----------
12345 Contoso Ltd 1599.99
Funkce prochází strukturu JSON pomocí výrazu 'cesta' a vrátí hodnotu jako NVARCHAR(4000) řetězec. Výsledek můžete přetypovat na jiné datové typy podle potřeby pro výpočty nebo porovnání.
JSON_QUERY() extrahuje objekt NEBO pole JSON (nekalarní hodnoty):
DECLARE @json NVARCHAR(MAX) = N'{
"customer": {
"id": 12345,
"name": "Contoso Ltd"
},
"items": [
{"product": "Widget", "qty": 5},
{"product": "Gadget", "qty": 3}
]
}';
SELECT
JSON_QUERY(@json, '$.customer') AS CustomerObject,
JSON_QUERY(@json, '$.items') AS ItemsArray;
Výsledek bude:
CustomerObject ItemsArray
-------------------------------------- ------------------------------------------------
{"id": 12345,"name": "Contoso Ltd"} [{"product": "Widget", "qty": 5},{"product": "Gadget", "qty": 3}]
Na rozdíl od JSON_VALUE()toho JSON_QUERY() zachovává strukturu JSON, vrací objekty a pole jako platné řetězce JSON, které můžete ukládat, předávat jiným funkcím nebo se vracet do aplikací.
Výraz cesty používá $ k reprezentaci kořenového prvku, s tečkovou notací pro vnořené vlastnosti a zápisem hranatých závorek pro prvky pole, jako je v následujícím příkladu:
-- Access array elements by index (0-based)
SELECT JSON_VALUE(@json, '$.items[0].product') AS FirstProduct;
Výsledkem bude:
FirstProduct
------------
Widget
Indexy pole začínají na 0, takže $.items[0] odkazuje na první prvek. Tuto syntaxi použijte k extrahování konkrétních položek, když znáte jejich pozici, nebo ji zkombinujte s OPENJSON, když potřebujete zpracovat všechny prvky pole.
Návod
Použijte JSON_VALUE() , když potřebujete skalární hodnotu pro porovnání nebo výpočty. Použijte JSON_QUERY() , když potřebujete zachovat strukturu JSON vnořených objektů nebo polí.
Parsování polí JSON pomocí OPENJSON
OPENJSON je funkce s hodnotou tabulky, která transformuje data JSON na relační sadu řádků. Pomocí této funkce můžete spojit data JSON s relačními tabulkami nebo zpracovat prvky pole jednotlivě.
Následující dotaz parsuje pole JSON do řádků s výchozím schématem:
DECLARE @json NVARCHAR(MAX) = N'[
{"id": 1, "name": "Widget", "price": 29.99},
{"id": 2, "name": "Gadget", "price": 49.99},
{"id": 3, "name": "Gizmo", "price": 19.99}
]';
SELECT * FROM OPENJSON(@json);
Výsledek bude:
key value type
--- -------------------------------------------- ----
0 {"id": 1, "name": "Widget", "price": 29.99} 5
1 {"id": 2, "name": "Gadget", "price": 49.99} 5
2 {"id": 3, "name": "Gizmo", "price": 19.99} 5
Bez schématu OPENJSON vrátí tři sloupce: key (název indexu pole nebo vlastnosti), value (obsah JSON) a type (číslo označující datový typ JSON: 0=null, 1=string, 2=number, 3=boolean, 4=array, 5=object).
Následující dotaz definuje explicitní schéma pro extrahování konkrétních sloupců se správnými datovými typy:
SELECT
ProductID,
ProductName,
Price
FROM OPENJSON(@json)
WITH (
ProductID INT '$.id',
ProductName NVARCHAR(100) '$.name',
Price DECIMAL(10,2) '$.price'
);
Výsledek bude:
ProductID ProductName Price
--------- ----------- ------
1 Widget 29.99
2 Gadget 49.99
3 Gizmo 19.99
Klauzule WITH mapuje vlastnosti JSON na zadané sloupce. Tento přístup poskytuje správné datové typy pro výpočty a porovnání a umožňuje vybrat pouze vlastnosti, které potřebujete.
Kombinování OPENJSON s tabulkovými daty pomocí CROSS APPLY:
-- Assuming Orders table has a JSON column called OrderDetails
SELECT
o.OrderID,
o.CustomerID,
items.ProductName,
items.Quantity,
items.UnitPrice
FROM Orders AS o
CROSS APPLY OPENJSON(o.OrderDetails)
WITH (
ProductName NVARCHAR(100) '$.product',
Quantity INT '$.qty',
UnitPrice DECIMAL(10,2) '$.price'
) AS items;
Poznámka:
Pokud kód používá OPENJSON spolu s CROSS APPLY, řádky z hlavní tabulky, které obsahují NULL nebo prázdné hodnoty JSON, se ve výsledcích nezobrazí. Použijte OUTER APPLY , pokud potřebujete zahrnout řádky bez dat JSON.
Vytvoření FORMÁTU JSON pomocí JSON_OBJECT a JSON_ARRAY
SQL Server 2022 představil JSON_OBJECT a JSON_ARRAY funkce pro intuitivní konstrukci JSON:
JSON_OBJECT() vytvoří objekt JSON z párů klíč-hodnota. Následující příklad ukazuje, jak vytvořit objekt JSON pro produkt:
SELECT JSON_OBJECT(
'id': ProductID,
'name': Name,
'price': ListPrice,
'available': CASE WHEN SellEndDate IS NULL THEN 'true' ELSE 'false' END
) AS ProductJson
FROM SalesLT.Product
WHERE ProductID = 680;
Výsledkem bude:
ProductJson
---------------------------------------------------------------------------
{"id":680,"name":"HL Road Frame - Black, 58","price":1431.50,"available":"true"}
Funkce automaticky zpracovává převod datového typu a správné escapování JSON pro speciální znaky v řetězcových hodnotách.
JSON_ARRAY() vytvoří pole JSON z hodnot. Následující příklad vytvoří pole JSON:
SELECT JSON_ARRAY(
'SQL Server',
'Azure SQL Database',
'SQL Database in Fabric'
) AS Platforms;
Výsledkem bude:
Platforms
---------------------------------------------------------
["SQL Server","Azure SQL Database","SQL Database in Fabric"]
Můžete předat hodnoty sloupců, proměnné nebo literály do JSON_ARRAY(). Funkce vytvoří správně naformátované pole JSON bez ohledu na vstupní typy.
Potom tyto funkce zkombinujte a sestavte vnořené struktury JSON. Následující příklad vytvoří kompletní objekt JSON objednávky s informacemi o zákaznících a součtech:
SELECT JSON_OBJECT(
'orderId': soh.SalesOrderID,
'orderDate': soh.OrderDate,
'customer': JSON_OBJECT(
'id': c.CustomerID,
'name': c.CompanyName
),
'totals': JSON_OBJECT(
'subtotal': soh.SubTotal,
'tax': soh.TaxAmt,
'total': soh.TotalDue
)
) AS OrderJson
FROM SalesLT.SalesOrderHeader AS soh
INNER JOIN SalesLT.Customer AS c
ON soh.CustomerID = c.CustomerID
WHERE soh.SalesOrderID = 71774;
Výsledkem bude:
OrderJson
--------------------------------------------------------------------------------
{"orderId":71774,"orderDate":"2008-06-01","customer":{"id":29825,"name":"Contoso"},"totals":{"subtotal":880.35,"tax":70.43,"total":972.79}}
Volání funkcí JSON_OBJECT vytváří hierarchické struktury, které odpovídají očekávanému formátu vaší aplikace. Tento přístup je čistější než zřetězení řetězců a zajišťuje platný výstup JSON.
Agregace dat pomocí JSON_ARRAYAGG
JSON_ARRAYAGG agreguje hodnoty z více řádků do jednoho pole JSON. Tato funkce je užitečná při vytváření denormalizovaného výstupu JSON z normalizovaných relačních dat:
SELECT
c.CustomerID,
c.CompanyName,
JSON_ARRAYAGG(soh.SalesOrderID) AS OrderIds
FROM SalesLT.Customer AS c
INNER JOIN SalesLT.SalesOrderHeader AS soh
ON c.CustomerID = soh.CustomerID
GROUP BY c.CustomerID, c.CompanyName;
Výsledkem bude:
CustomerID CompanyName OrderIds
---------- ------------------- ------------------
29825 Contoso Retail [71774,71776,71780]
29847 Adventure Works [71782,71784]
Funkce shromažďuje všechny odpovídající hodnoty ze seskupených řádků a kombinuje je do jednoho pole JSON. Tato metoda je užitečná při vytváření denormalizovaných odpovědí rozhraní API z normalizovaných databázových tabulek.
Pomocí kombinace JSON_ARRAYAGGJSON_OBJECT můžete vytvářet pole komplexních objektů:
SELECT
pc.Name AS Category,
JSON_ARRAYAGG(
JSON_OBJECT(
'id': p.ProductID,
'name': p.Name,
'price': p.ListPrice
)
) AS Products
FROM SalesLT.ProductCategory AS pc
INNER JOIN SalesLT.Product AS p
ON pc.ProductCategoryID = p.ProductCategoryID
GROUP BY pc.ProductCategoryID, pc.Name;
Následující výsledek bude:
Category Products
-------------- --------------------------------------------------------------------------
Road Bikes [{"id":749,"name":"Road-150 Red, 62","price":3578.27},{"id":750,"name":"Road-150 Red, 44","price":3578.27}]
Mountain Bikes [{"id":771,"name":"Mountain-100 Silver, 38","price":3399.99},{"id":772,"name":"Mountain-100 Black, 38","price":3374.99}]
Důležité
JSON_ARRAYAGG a JSON_OBJECT/JSON_ARRAY funkce jsou dostupné v SQL Serveru 2022 a novějších, Azure SQL Database a databázích SQL v Microsoft Fabric. V dřívějších verzích použijte FOR JSON PATH pro podobnou funkčnost.
Ověření a kontrola FORMÁTU JSON pomocí JSON_CONTAINS
Data JSON z externích zdrojů můžou být poškozená, chybí očekávané vlastnosti nebo obsahují neočekávané hodnoty. Pokus o extrakci hodnot z neplatného formátu JSON nebo chybějících cest může způsobit selhání dotazů nebo vrátit zavádějící NULL výsledky, které maskují problémy s daty.
Robustní zpracování JSON vyžaduje defenzivní kódování: Před parsováním ověřte, že je JSON správně vytvořený. Před extrahováním hodnot zkontrolujte, jestli existují očekávané cesty, a před použitím obchodní logiky ověřte, že hodnoty odpovídají vašim očekáváním. SQL Server poskytuje několik funkcí, které vám pomůžou ověřit obsah JSON v každé fázi zpracování.
Principy laxních a striktních režimů cest
Výrazy cesty JSON můžete použít ve dvou režimech, které řídí zpracování chyb:
DECLARE @json NVARCHAR(MAX) = N'{"name": "Widget", "price": 29.99}';
-- Lax mode (default): Returns NULL for missing paths
SELECT JSON_VALUE(@json, 'lax $.description') AS LaxResult;
-- Strict mode: Raises an error for missing paths
SELECT JSON_VALUE(@json, 'strict $.description') AS StrictResult;
Výsledkem bude:
LaxResult
---------
NULL
-- Strict mode raises: Property cannot be found on the specified JSON path.
Použijte režim lax (výchozí) v případech, kdy se očekává, že budou některé vlastnosti chybět, a mělo by se vrátit NULL. Použijte režim strict, pokud chybějící vlastnosti označují problém s daty, který by měl vyvolat chybu.
ISJSON ověří, jestli řetězec obsahuje platný JSON. Následující příklad ukazuje, jak používat ISJSON:
SELECT
ISJSON('{"name": "test"}') AS ValidJson, -- Returns 1
ISJSON('not valid json') AS InvalidJson, -- Returns 0
ISJSON(NULL) AS NullJson; -- Returns NULL
Výsledkem bude:
ValidJson InvalidJson NullJson
--------- ----------- --------
1 0 NULL
K řádnému zpracování neplatných dat použijte ISJSON klauzule v WHERE klauzulích k filtrování řádků s platným kódem JSON nebo ve CASE výrazech.
JSON_PATH_EXISTS zkontroluje, jestli v dokumentu JSON existuje konkrétní cesta, například v následujícím příkladu:
DECLARE @json NVARCHAR(MAX) = N'{"customer": {"name": "Contoso", "tier": "Gold"}}';
SELECT
JSON_PATH_EXISTS(@json, '$.customer.name') AS HasName,
JSON_PATH_EXISTS(@json, '$.customer.email') AS HasEmail;
Výsledkem bude:
HasName HasEmail
------- --------
1 0
Tato funkce vrátí hodnotu 1, pokud cesta existuje, 0, pokud neexistuje. Použijte ho před voláním JSON_VALUE v přísném režimu nebo pro podmíněné zpracovávání JSON s různými strukturami.
Slouží JSON_CONTAINS ke kontrole, jestli dokument JSON obsahuje konkrétní hodnotu nebo objekt, například v následujícím příkladu:
DECLARE @json NVARCHAR(MAX) = N'{"tags": ["sql", "database", "azure"]}';
SELECT
JSON_CONTAINS(@json, '"sql"', '$.tags') AS HasSqlTag,
JSON_CONTAINS(@json, '"python"', '$.tags') AS HasPythonTag;
Výsledkem bude:
HasSqlTag HasPythonTag
--------- ------------
1 0
Optimalizace dotazů JSON pomocí počítaných sloupců
Když často dotazujete konkrétní vlastnosti JSON, databázový stroj musí analyzovat dokument JSON pro každý řádek každého dotazu. U tabulek s tisíci nebo miliony řádků tato opakovaná analýza vytváří významnou režii. Počítané sloupce umožňují extrahovat hodnoty JSON jednou a uložit je do dotazovatelného formátu, který podporuje indexování.
Proč analýza JSON ovlivňuje výkon
Představte si tabulku s 100 000 záznamy o produktech, kde každý řádek obsahuje dokument JSON s atributy produktu. Filtrování dotazu podle kategorie musí:
- Přečtěte každý řádek z tabulky
- Pokud chcete najít vlastnost kategorie, parsujte dokument JSON.
- Extrahujte a porovnejte hodnotu
Bez optimalizace vyžadují i jednoduché filtry úplné prohledávání tabulek s parsováním JSON na každém řádku.
Vytvoření počítaných sloupců pro vlastnosti JSON
Počítaný sloupec automaticky extrahuje vlastnost JSON a zpřístupní ji jako běžný sloupec, například v následujícím příkladu:
-- Add a computed column that extracts a JSON property
ALTER TABLE Products
ADD ProductCategory AS JSON_VALUE(ProductData, '$.category');
-- The column is now available in queries
SELECT ProductID, ProductName, ProductCategory
FROM Products
WHERE ProductCategory = 'Electronics';
Výsledkem bude:
ProductID ProductName ProductCategory
--------- ------------------- ---------------
101 Wireless Mouse Electronics
102 USB Keyboard Electronics
103 HD Monitor Electronics
Ve výchozím nastavení jsou vypočítané sloupce virtuální. Databáze vypočítá hodnotu v době dotazu, ale dokáže optimalizovat extrakci JSON. Pokud chcete dosáhnout ještě lepšího výkonu, můžete počítaný sloupec zachovat jako v následujícím příkladu:
-- Persisted computed column stores the extracted value physically
ALTER TABLE Products
ADD ProductCategory AS JSON_VALUE(ProductData, '$.category') PERSISTED;
Trvalé sloupce ukládají extrahované hodnoty na disk, takže JSON se parsuje jenom během INSERT a UPDATE operacích, ne během SELECT dotazů.
Přidání indexů pro rychlejší filtrování
Skutečný zisk z výkonu pochází z indexování vypočítaných sloupců:
-- Create an index on the computed column
CREATE INDEX IX_Products_Category ON Products(ProductCategory);
-- Now this query uses an index seek instead of a table scan
SELECT ProductID, ProductName
FROM Products
WHERE ProductCategory = 'Electronics';
Bez indexu dotaz prohledá všech 100 000 řádků. Pomocí indexu dotazovací modul provede hledání indexu a načte pouze odpovídající řádky. Tento index může zkrátit dobu dotazu z sekund na milisekundy.
Indexování více vlastností JSON
Pro dotazy, které filtrují více vlastností JSON, vytvořte počítané sloupce a složený index:
-- Extract multiple properties
ALTER TABLE Products
ADD ProductCategory AS JSON_VALUE(ProductData, '$.category') PERSISTED,
ProductBrand AS JSON_VALUE(ProductData, '$.brand') PERSISTED,
ProductPrice AS CAST(JSON_VALUE(ProductData, '$.price') AS DECIMAL(10,2)) PERSISTED;
-- Create a composite index for common query patterns
CREATE INDEX IX_Products_Category_Brand ON Products(ProductCategory, ProductBrand);
-- Create an index for price range queries
CREATE INDEX IX_Products_Price ON Products(ProductPrice);
Teď dotazy filtrující podle kategorie a značky nebo řazení podle ceny můžou tyto indexy efektivně používat.
Návod
U často přístupných vlastností JSON můžou vypočítané sloupce s indexy zlepšit výkon dotazů v porovnání s parsováním JSON v době dotazu. Monitorujte vzory dotazů a vytvořte počítané sloupce pro vlastnosti používané v WHEREklauzulích , JOINnebo ORDER BY klauzulích.
Transformace relačních dat na JSON pomocí formátu FOR JSON
Pro komplexní výstup JSON z dotazů použijte:FOR JSON PATHFOR JSON AUTO
SELECT
p.ProductID,
p.Name,
p.ListPrice,
pc.Name AS CategoryName
FROM SalesLT.Product AS p
INNER JOIN SalesLT.ProductCategory AS pc
ON p.ProductCategoryID = pc.ProductCategoryID
WHERE p.ListPrice > 1000
FOR JSON PATH, ROOT('products');
Výsledkem bude:
{"products":[{"ProductID":749,"Name":"Road-150 Red, 62","ListPrice":3578.27,"CategoryName":"Road Bikes"},{"ProductID":750,"Name":"Road-150 Red, 44","ListPrice":3578.27,"CategoryName":"Road Bikes"}]}
FOR JSON PATH poskytuje kontrolu nad strukturou JSON prostřednictvím aliasů sloupců. K vytvoření vnořených objektů použijte zápis tečky v aliasech:
SELECT
p.ProductID AS 'product.id',
p.Name AS 'product.name',
pc.Name AS 'product.category'
FROM SalesLT.Product AS p
INNER JOIN SalesLT.ProductCategory AS pc
ON p.ProductCategoryID = pc.ProductCategoryID
WHERE p.ProductID = 680
FOR JSON PATH;
Výsledkem bude:
[{"product":{"id":680,"name":"HL Road Frame - Black, 58","category":"Road Frames"}}]
Alias 'product.id' sloupce vytvoří vnořený product objekt s id vlastností. Tato technika umožňuje tvarovat výstup tak, aby odpovídal očekávanému formátu rozhraní API bez následného zpracování.
Další informace o funkcích JSON na SQL Serveru najdete v tématu Data JSON v SQL Serveru a funkcích JSON.