sql >> Databáze >  >> RDS >> Sqlserver

Úvod do OPENJSON s příklady (SQL Server)

SQL Server má funkci s hodnotou tabulky nazvanou OPENJSON() který vytváří relační pohled na data JSON.

Když jej zavoláte, předáte dokument JSON jako argument a OPENJSON() poté jej analyzuje a vrátí objekty a vlastnosti dokumentu JSON v tabulkovém formátu – jako řádky a sloupce.

Příklad

Zde je jednoduchý příklad k demonstraci.

SELECT * FROM OPENJSON('["Cat","Dog","Bird"]');

Výsledek:

+-------+---------+--------+
| key   | value   | type   |
|-------+---------+--------|
| 0     | Cat     | 1      |
| 1     | Dog     | 1      |
| 2     | Bird    | 1      |
+-------+---------+--------+

Ve výchozím nastavení OPENJSON() vrátí tabulku se třemi sloupci; klíč , hodnota a zadejte .

Máte také možnost zadat své vlastní schéma (což znamená, že můžete definovat své vlastní sloupce). V mém jednoduchém příkladu jsem použil výchozí schéma, a tak byly vráceny tři výchozí sloupce.

Tyto sloupce jsou definovány takto:

Sloupec Popis
klíč Obsahuje název zadané vlastnosti nebo index prvku v zadaném poli. Toto je nvarchar(4000) hodnotu a sloupec má řazení BIN2.
hodnota Obsahuje hodnotu vlastnosti. Toto je nvarchar(max) hodnotu a sloupec zdědí své řazení z poskytnutého JSON.
typ Obsahuje typ JSON hodnoty. Toto je reprezentováno jako int hodnota (od 05 ). Tento sloupec je vrácen pouze při použití výchozího schématu.

Výchozí typy

Ve světě JSON existuje šest datových typů. Jedná se o řetězec , číslo , pravda/nepravda (logická hodnota), null , objekt a pole .

Když analyzujete nějaký JSON prostřednictvím OPENJSON() pomocí výchozího schématu OPENJSON() zjistí, jaký je typ JSON, a poté vyplní typ sloupec s int hodnotu, která tento typ představuje.

int hodnota se proto může pohybovat od 05 . Každý int value představuje typ JSON, jak je uvedeno v následující tabulce.

Hodnota ve sloupci „typ“ Datový typ JSON
0 null
1 řetězec
2 číslo
3 pravda/nepravda
4 pole
5 objekt

Následující příklad vrátí všech šest těchto typů JSON.

SELECT * FROM OPENJSON('{"name" : null}');
SELECT * FROM OPENJSON('["Cat","Dog","Bird"]');
SELECT * FROM OPENJSON('[1,2,3]');
SELECT * FROM OPENJSON('[true,false]');
SELECT * FROM OPENJSON('{"cats":[{ "id":1, "name":"Fluffy"},{ "id":2, "name":"Scratch"}]}');
SELECT * FROM OPENJSON('[{"A":1,"B":0,"C":1}]');

Výsledek:

+-------+---------+--------+
| key   | value   | type   |
|-------+---------+--------|
| name  | NULL    | 0      |
+-------+---------+--------+
(1 row affected)
+-------+---------+--------+
| key   | value   | type   |
|-------+---------+--------|
| 0     | Cat     | 1      |
| 1     | Dog     | 1      |
| 2     | Bird    | 1      |
+-------+---------+--------+
(3 rows affected)
+-------+---------+--------+
| key   | value   | type   |
|-------+---------+--------|
| 0     | 1       | 2      |
| 1     | 2       | 2      |
| 2     | 3       | 2      |
+-------+---------+--------+
(3 rows affected)
+-------+---------+--------+
| key   | value   | type   |
|-------+---------+--------|
| 0     | true    | 3      |
| 1     | false   | 3      |
+-------+---------+--------+
(2 rows affected)
+-------+----------------------------------------------------------+--------+
| key   | value                                                    | type   |
|-------+----------------------------------------------------------+--------|
| cats  | [{ "id":1, "name":"Fluffy"},{ "id":2, "name":"Scratch"}] | 4      |
+-------+----------------------------------------------------------+--------+
(1 row affected)
+-------+---------------------+--------+
| key   | value               | type   |
|-------+---------------------+--------|
| 0     | {"A":1,"B":0,"C":1} | 5      |
+-------+---------------------+--------+
(1 row affected)

Vrátit vnořený JSON

Vnořený objekt nebo pole můžete vrátit zadáním jeho cesty jako volitelného druhého argumentu.

Jinými slovy, nemusíte analyzovat celý dokument JSON – můžete se rozhodnout analyzovat pouze tu část, která vás zajímá.

Zde je příklad.

DECLARE @json NVARCHAR(4000) = N'{ 
    "pets" : {
            "cats" : [
            { "id" : 1, "name" : "Fluffy", "sex" : "Female" },
            { "id" : 2, "name" : "Long Tail", "sex" : "Female" },
            { "id" : 3, "name" : "Scratch", "sex" : "Male" }
        ],
            "dogs" : [
            { "name" : "Fetch", "sex" : "Male" },
            { "name" : "Fluffy", "sex" : "Male" },
            { "name" : "Wag", "sex" : "Female" }
        ]
    }
}';
SELECT * FROM OPENJSON(@json, '$.pets.cats');

Výsledek:

+-------+------------------------------------------------------+--------+
| key   | value                                                | type   |
|-------+------------------------------------------------------+--------|
| 0     | { "id" : 1, "name" : "Fluffy", "sex" : "Female" }    | 5      |
| 1     | { "id" : 2, "name" : "Long Tail", "sex" : "Female" } | 5      |
| 2     | { "id" : 3, "name" : "Scratch", "sex" : "Male" }     | 5      |
+-------+------------------------------------------------------+--------+

V tomto případě jsem zadal cestu $.pets.cats , což vedlo pouze k hodnotě koček vracení. Hodnota koček je pole, takže bylo vráceno celé pole.

Chcete-li vrátit pouze jednu kočku (tj. jeden prvek pole), můžeme pro vrácení hodnot pole použít syntaxi hranatých závorek (jako tento $.pets.cats[1] ).

Zde je stejný příklad upravený tak, aby vrátil pouze jeden prvek pole:

DECLARE @json NVARCHAR(4000) = N'{ 
    "pets" : {
            "cats" : [
            { "id" : 1, "name" : "Fluffy", "sex" : "Female" },
            { "id" : 2, "name" : "Long Tail", "sex" : "Female" },
            { "id" : 3, "name" : "Scratch", "sex" : "Male" }
        ],
            "dogs" : [
            { "name" : "Fetch", "sex" : "Male" },
            { "name" : "Fluffy", "sex" : "Male" },
            { "name" : "Wag", "sex" : "Female" }
        ]
    }
}';
SELECT * FROM OPENJSON(@json, '$.pets.cats[1]');

Výsledek:

+-------+-----------+--------+
| key   | value     | type   |
|-------+-----------+--------|
| id    | 2         | 2      |
| name  | Long Tail | 1      |
| sex   | Female    | 1      |
+-------+-----------+--------+

Indexy pole JSON jsou založeny na nule, takže tento příklad vrátil druhou hodnotu pole (protože jsem zadal $.pets.cats[1] ).

Kdybych zadal $.pets.cats[0] , byla by vrácena první hodnota (tj. kočka s názvem „Fluffy“).

Definujte schéma

Jak již bylo zmíněno, můžete zadat své vlastní schéma (tj. definovat vlastní sloupce a typy).

Zde je příklad, jak to udělat.

DECLARE @json NVARCHAR(4000) = N'{ 
    "pets" : {
            "cats" : [
            { "id" : 1, "name" : "Fluffy", "sex" : "Female" },
            { "id" : 2, "name" : "Long Tail", "sex" : "Female" },
            { "id" : 3, "name" : "Scratch", "sex" : "Male" }
        ],
            "dogs" : [
            { "name" : "Fetch", "sex" : "Male" },
            { "name" : "Fluffy", "sex" : "Male" },
            { "name" : "Wag", "sex" : "Female" }
        ]
    }
}';

SELECT * FROM OPENJSON(@json, '$.pets.cats')
WITH  (
        [Cat Id]    int             '$.id',  
        [Cat Name]  varchar(60)     '$.name', 
        [Sex]       varchar(6)      '$.sex', 
        [Cats]      nvarchar(max)   '$' AS JSON   
    );

Výsledek:

+----------+------------+--------+------------------------------------------------------+
| Cat Id   | Cat Name   | Sex    | Cats                                                 |
|----------+------------+--------+------------------------------------------------------|
| 1        | Fluffy     | Female | { "id" : 1, "name" : "Fluffy", "sex" : "Female" }    |
| 2        | Long Tail  | Female | { "id" : 2, "name" : "Long Tail", "sex" : "Female" } |
| 3        | Scratch    | Male   | { "id" : 3, "name" : "Scratch", "sex" : "Male" }     |
+----------+------------+--------+------------------------------------------------------+

Vidíme, že názvy sloupců odpovídají těm, které jsem zadal v WITH doložka. V této klauzuli jsem namapoval každý klíč JSON na vlastní preferované názvy sloupců. Také jsem určil typ dat serveru SQL Server, který chci pro každý sloupec.

Také jsem použil AS JSON na posledním sloupci, aby se tento sloupec vrátil jako fragment JSON. Když používáte AS JSON, typ dat musí být nvarchar(max) .

Ověřte datové typy

K ověření datových typů každého sloupce můžeme použít následující dotaz.

Tento dotaz používá sys.dm_exec_describe_first_result_set zobrazení dynamické správy systému, které vrací metadata o první sadě výsledků z dotazu.

DECLARE @json NVARCHAR(4000) = N'{ 
    "pets" : {
            "cats" : [
            { "id" : 1, "name" : "Fluffy", "sex" : "Female" },
            { "id" : 2, "name" : "Long Tail", "sex" : "Female" },
            { "id" : 3, "name" : "Scratch", "sex" : "Male" }
        ],
            "dogs" : [
            { "name" : "Fetch", "sex" : "Male" },
            { "name" : "Fluffy", "sex" : "Male" },
            { "name" : "Wag", "sex" : "Female" }
        ]
    }
}';

SELECT 
    name,
    system_type_name
FROM sys.dm_exec_describe_first_result_set(
    'SELECT * FROM OPENJSON(@json, ''$.pets.cats'') WITH  (
        [Cat Id]    int             ''$.id'',  
        [Cat Name]  varchar(60)     ''$.name'', 
        [Sex]       varchar(6)      ''$.sex'', 
        [Cats]      nvarchar(max)   ''$'' AS JSON 
    )',
    null,
    0
);

Výsledek:

+----------+--------------------+
| name     | system_type_name   |
|----------+--------------------|
| Cat Id   | int                |
| Cat Name | varchar(60)        |
| Sex      | varchar(6)         |
| Cats     | nvarchar(max)      |
+----------+--------------------+

Vidíme, že dokonale odpovídají mému schématu.

Všimněte si, že klíč , hodnota a zadejte Při definování vlastního schématu nejsou sloupce dostupné. Tyto sloupce jsou dostupné pouze při použití výchozího schématu.

Vložte analyzovaný JSON do tabulky

Nyní si možná myslíte, že bychom mohli náš analyzovaný JSON snadno vložit do databázové tabulky.

A měli byste pravdu.

Již jsme jej připravili se sloupci a řádky a dokonce jsme sloupce pojmenovali a dali jim datové typy.

Nyní je čas jej vložit do tabulky.

DECLARE @json NVARCHAR(4000) = N'{ 
    "pets" : {
            "cats" : [
            { "id" : 1, "name" : "Fluffy", "sex" : "Female" },
            { "id" : 2, "name" : "Long Tail", "sex" : "Female" },
            { "id" : 3, "name" : "Scratch", "sex" : "Male" }
        ],
            "dogs" : [
            { "name" : "Fetch", "sex" : "Male" },
            { "name" : "Fluffy", "sex" : "Male" },
            { "name" : "Wag", "sex" : "Female" }
        ]
    }
}';

SELECT * INTO JsonCats
FROM OPENJSON(@json, '$.pets.cats')
WITH  (
        [Cat Id]    int             '$.id',  
        [Cat Name]  varchar(60)     '$.name', 
        [Sex]       varchar(6)      '$.sex', 
        [Cats]      nvarchar(max)   '$' AS JSON   
    );

Vše, co jsem udělal, bylo přidat INTO JsonCats na můj dotaz, za účelem vytvoření tabulky s názvem JsonCats a vložte do něj výsledky dotazu.

Nyní vybereme obsah této tabulky.

SELECT * FROM JsonCats;

Výsledek:

+----------+------------+--------+------------------------------------------------------+
| Cat Id   | Cat Name   | Sex    | Cats                                                 |
|----------+------------+--------+------------------------------------------------------|
| 1        | Fluffy     | Female | { "id" : 1, "name" : "Fluffy", "sex" : "Female" }    |
| 2        | Long Tail  | Female | { "id" : 2, "name" : "Long Tail", "sex" : "Female" } |
| 3        | Scratch    | Male   | { "id" : 3, "name" : "Scratch", "sex" : "Male" }     |
+----------+------------+--------+------------------------------------------------------+

Obsah je přesně takový, jaký jsme viděli v předchozím příkladu.

A pro naprostou jistotu nyní můžeme použít sys.column zobrazení systémového katalogu zkontrolujte názvy a typy sloupců tabulky.

SELECT
    name AS [Column],
    TYPE_NAME(system_type_id) AS [Type],
    max_length
FROM sys.columns 
WHERE OBJECT_ID('JsonCats') = object_id;

Výsledek:

+----------+----------+--------------+
| Column   | Type     | max_length   |
|----------+----------+--------------|
| Cat Id   | int      | 4            |
| Cat Name | varchar  | 60           |
| Sex      | varchar  | 6            |
| Cats     | nvarchar | -1           |
+----------+----------+--------------+

Opět přesně tak, jak jsme to specifikovali.

Všimněte si, že sys.columns vždy vrátí max_length z -1 když je datový typ sloupce varchar(max) , nvarchar(max) , varbinary(max) nebo xml . Zadali jsme nvarchar(max) a tedy hodnotu -1 je přesně podle očekávání.

Režim cesty:Laxní versus přísný

Cesta uvedená ve druhém argumentu nebo v WITH klauzule může (volitelně) začínat lax nebo strict klíčové slovo.

  • V lax režim, OPENJSON() nevyvolá chybu, pokud nelze objekt nebo hodnotu na zadané cestě nalézt. Pokud cestu nelze najít, OPENJSON() vrátí buď prázdnou sadu výsledků, nebo NULL hodnotu.
  • V strict režim, OPENJSON() vrátí chybu, pokud cestu nelze najít.

Výchozí hodnota je lax , takže pokud neurčíte režim cesty, lax bude použit režim.

Zde je několik příkladů, které demonstrují, co se stane s každým režimem, když nelze najít cestu.

Druhý argument

V následujících dvou příkladech uvádím neexistující cestu ve druhém argumentu při volání OPENJSON() . První příklad ukazuje, co se stane při použití laxního režimu, druhý příklad ukazuje, co se stane při použití přísného režimu.

Laxní režim

Zde je to, co se děje v lax režimu, když cestu nelze najít.

DECLARE @json NVARCHAR(4000) = N'{ 
    "pets" : {
            "cats" : [
            { "id" : 1, "name" : "Fluffy", "sex" : "Female" },
            { "id" : 2, "name" : "Long Tail", "sex" : "Female" },
            { "id" : 3, "name" : "Scratch", "sex" : "Male" }
        ],
            "dogs" : [
            { "name" : "Fetch", "sex" : "Male" },
            { "name" : "Fluffy", "sex" : "Male" },
            { "name" : "Wag", "sex" : "Female" }
        ]
    }
}';
SELECT * FROM OPENJSON(@json, 'lax $.pets.cows');

Výsledek:

(0 rows affected)

Žádná chyba. Vrátily se pouze nulové výsledky.

Přísný režim

Nyní je zde v strict režimu.

DECLARE @json NVARCHAR(4000) = N'{ 
    "pets" : {
            "cats" : [
            { "id" : 1, "name" : "Fluffy", "sex" : "Female" },
            { "id" : 2, "name" : "Long Tail", "sex" : "Female" },
            { "id" : 3, "name" : "Scratch", "sex" : "Male" }
        ],
            "dogs" : [
            { "name" : "Fetch", "sex" : "Male" },
            { "name" : "Fluffy", "sex" : "Male" },
            { "name" : "Wag", "sex" : "Female" }
        ]
    }
}'
SELECT * FROM OPENJSON(@json, 'strict $.pets.cows');

Výsledek:

Msg 13608, Level 16, State 3, Line 15
Property cannot be found on the specified JSON path.

Podle očekávání vedl přísný režim k chybě.

V klauzuli WITH

V následujících dvou příkladech opět testujeme laxní režim vs. přísný režim, tentokrát to však specifikujeme v WITH klauzule při definování schématu.

Laxní režim

Zde je to, co se děje v lax režimu.

DECLARE @json NVARCHAR(4000) = N'{ 
    "pets" : {
            "cats" : [
            { "id" : 1, "name" : "Fluffy", "sex" : "Female" },
            { "id" : 2, "name" : "Long Tail", "sex" : "Female" },
            { "id" : 3, "name" : "Scratch", "sex" : "Male" }
        ],
            "dogs" : [
            { "name" : "Fetch", "sex" : "Male" },
            { "name" : "Fluffy", "sex" : "Male" },
            { "name" : "Wag", "sex" : "Female" }
        ]
    }
}';

SELECT *
FROM OPENJSON(@json, '$.pets.cats')
WITH  (
        [Cat Id]    int             '$.id',  
        [Cat Name]  varchar(60)     '$.name', 
        [Born]      date            'lax $.born', 
        [Cats]      nvarchar(max)   '$' AS JSON   
    );

Výsledek:

+----------+------------+--------+------------------------------------------------------+
| Cat Id   | Cat Name   | Born   | Cats                                                 |
|----------+------------+--------+------------------------------------------------------|
| 1        | Fluffy     | NULL   | { "id" : 1, "name" : "Fluffy", "sex" : "Female" }    |
| 2        | Long Tail  | NULL   | { "id" : 2, "name" : "Long Tail", "sex" : "Female" } |
| 3        | Scratch    | NULL   | { "id" : 3, "name" : "Scratch", "sex" : "Male" }     |
+----------+------------+--------+------------------------------------------------------+

V tomto případě používám 'lax $.born' protože se snažím odkazovat na klíč s názvem born , ale takový klíč v JSON neexistuje.

Tentokrát má sloupec, který nelze najít, za následek NULL hodnotu.

Přísný režim

Nyní je zde v strict režimu.

DECLARE @json NVARCHAR(4000) = N'{ 
    "pets" : {
            "cats" : [
            { "id" : 1, "name" : "Fluffy", "sex" : "Female" },
            { "id" : 2, "name" : "Long Tail", "sex" : "Female" },
            { "id" : 3, "name" : "Scratch", "sex" : "Male" }
        ],
            "dogs" : [
            { "name" : "Fetch", "sex" : "Male" },
            { "name" : "Fluffy", "sex" : "Male" },
            { "name" : "Wag", "sex" : "Female" }
        ]
    }
}';

SELECT *
FROM OPENJSON(@json, '$.pets.cats')
WITH  (
        [Cat Id]    int             '$.id',  
        [Cat Name]  varchar(60)     '$.name', 
        [Born]      date            'strict $.born', 
        [Cats]      nvarchar(max)   '$' AS JSON   
    );

Výsledek:

Msg 13608, Level 16, State 6, Line 16
Property cannot be found on the specified JSON path.

Tentokrát jsem použil 'strict $.born' .

Podle očekávání vedl přísný režim k chybě.

Úroveň kompatibility

OPENJSON() Funkce je dostupná pouze při úrovni kompatibility 130 nebo vyšší.

Pokud je úroveň kompatibility vaší databáze nižší než 130, SQL Server nebude schopen najít a spustit OPENJSON() a zobrazí se chyba.

Úroveň kompatibility databáze můžete zkontrolovat prostřednictvím sys.databases zobrazení katalogu.

Úroveň jeho kompatibility můžete změnit takto:

ALTER DATABASE DatabaseName 
SET COMPATIBILITY_LEVEL = 150;

Nová verze JSON?

Pokud nejste s JSON tak obeznámeni, podívejte se na můj JSON tutoriál na Quackit.


  1. Vynutit omezení cizího klíče na sloupce stejné tabulky

  2. NENÍ IN vs NEEXISTUJE

  3. Odložitelná omezení v SQL Server

  4. SQL nerozpozná alias sloupce v klauzuli where