sql >> Database teknologi >  >> RDS >> Sqlserver

Introduktion til OPENJSON med eksempler (SQL-server)

SQL Server har en tabelværdi-funktion kaldet OPENJSON() der skaber en relationel visning af JSON-data.

Når du kalder det, sender du et JSON-dokument som et argument og OPENJSON() parser det derefter og returnerer JSON-dokumentets objekter og egenskaber i et tabelformat – som rækker og kolonner.

Eksempel

Her er et simpelt eksempel at demonstrere.

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

Resultat:

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

Som standard er OPENJSON() returnerer en tabel med tre kolonner; tast , værdi , og skriv .

Du har også mulighed for at angive dit eget skema (hvilket betyder, at du kan definere dine egne kolonner). I mit simple eksempel brugte jeg standardskemaet, og derfor blev de tre standardkolonner returneret.

Disse kolonner er defineret som følger:

Kolonne Beskrivelse
nøgle Indeholder navnet på den specificerede egenskab eller indekset for elementet i det specificerede array. Dette er en nvarchar(4000) værdi, og kolonnen har en BIN2-sortering.
værdi Indeholder ejendommens værdi. Dette er en nvarchar(max) værdi, og kolonnen arver sin sortering fra den angivne JSON.
type Indeholder værdiens JSON-type. Dette er repræsenteret som en int værdi (fra 0 til 5 ). Denne kolonne returneres kun, når du bruger standardskemaet.

Standardtyper

I JSON-verdenen er der seks datatyper. Disse er streng , nummer , sand/falsk (boolesk), nul , objekt og array .

Når du parser noget JSON gennem OPENJSON() ved at bruge standardskemaet, OPENJSON() finder ud af, hvad JSON-typen er, og udfylder derefter typen kolonne med en int værdi, der repræsenterer den type.

int værdi kan derfor variere fra 0 til 5 . Hver int værdi repræsenterer en JSON-type som skitseret i følgende tabel.

Værdi i kolonnen "type" JSON-datatype
0 null
1 streng
2 nummer
3 sand/falsk
4 array
5 objekt

Følgende eksempel returnerer alle seks af disse JSON-typer.

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}]');

Resultat:

+-------+---------+--------+
| 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)

Returner indlejret JSON

Du kan returnere et indlejret objekt eller array ved at angive dets sti som et valgfrit andet argument.

Du behøver med andre ord ikke at parse hele JSON-dokumentet – du kan vælge at parse kun den del, du er interesseret i.

Her er et eksempel.

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');

Resultat:

+-------+------------------------------------------------------+--------+
| 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      |
+-------+------------------------------------------------------+--------+

I dette tilfælde specificerede jeg en sti til $.pets.cats , hvilket kun resulterede i værdien af ​​katte bliver returneret. Værdien af ​​katte er et array, så hele arrayet blev returneret.

For at returnere kun én kat (dvs. ét array-element), kan vi bruge den firkantede parentes-syntaks til at returnere array-værdier (som denne $.pets.cats[1] ).

Her er det samme eksempel ændret til kun at returnere ét array-element:

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]');

Resultat:

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

JSON array indekser er nul-baserede, så dette eksempel returnerede den anden array værdi (fordi jeg specificerede $.pets.cats[1] ).

Hvis jeg havde angivet $.pets.cats[0] , ville den første værdi være blevet returneret (dvs. katten ved navn "Fluffy").

Definer et skema

Som nævnt kan du angive dit eget skema (dvs. definere dine egne kolonner og typer).

Her er et eksempel på at gøre det.

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   
    );

Resultat:

+----------+------------+--------+------------------------------------------------------+
| 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" }     |
+----------+------------+--------+------------------------------------------------------+

Vi kan se, at kolonnenavnene afspejler dem, som jeg har angivet i WITH klausul. I den klausul mappede jeg hver JSON-nøgle til mine egne foretrukne kolonnenavne. Jeg har også specificeret den SQL Server-datatype, som jeg ønsker for hver kolonne.

Jeg brugte også AS JSON på den sidste kolonne for at returnere den kolonne som et JSON-fragment. Når du bruger AS JSON, skal datatypen være nvarchar(max) .

Bekræft datatyperne

Vi kan bruge følgende forespørgsel til at verificere datatyperne for hver kolonne.

Denne forespørgsel bruger sys.dm_exec_describe_first_result_set system dynamisk administrationsvisning, som returnerer metadata om det første resultatsæt fra en forespørgsel.

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
);

Resultat:

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

Vi kan se, at de passer perfekt til mit skema.

Bemærk, at tasten , værdi , og skriv kolonner er ikke tilgængelige, når du definerer dit eget skema. Disse kolonner er kun tilgængelige, når du bruger standardskemaet.

Indsæt den parsede JSON i en tabel

Nu tænker du måske, at vi nemt kunne indsætte vores parsede JSON i en databasetabel.

Og du har ret.

Vi har allerede forberedt det med kolonner og rækker, og vi har endda navngivet kolonnerne og givet dem datatyper.

Nu er det tid til at indsætte det i en tabel.

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   
    );

Alt jeg gjorde var at tilføje INTO JsonCats til min forespørgsel, for at oprette en tabel kaldet JsonCats og indsæt resultaterne af forespørgslen i den.

Lad os nu vælge indholdet af den tabel.

SELECT * FROM JsonCats;

Resultat:

+----------+------------+--------+------------------------------------------------------+
| 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" }     |
+----------+------------+--------+------------------------------------------------------+

Indholdet er nøjagtigt, som vi så dem i det tidligere eksempel.

Og for at være helt sikker kan vi nu bruge sys.column systemkatalogvisning kontroller tabellens kolonnenavne og -typer.

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

Resultat:

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

Igen, præcis hvordan vi havde specificeret det.

Bemærk, at sys.columns returnerer altid en max_length af -1 når kolonnedatatypen er varchar(max) , nvarchar(max) , varbinary(max) eller xml . Vi specificerede nvarchar(max) og så værdien af ​​-1 er nøjagtigt som forventet.

Stitilstand:Lax vs Strict

Stien angivet i det andet argument eller i WITH klausul kan (valgfrit) starte med lax eller strict søgeord.

  • I lax tilstand, OPENJSON() rejser ikke en fejl, hvis objektet eller værdien på den angivne sti ikke kan findes. Hvis stien ikke kan findes, OPENJSON() returnerer enten et tomt resultatsæt eller en NULL værdi.
  • I strict tilstand, OPENJSON() returnerer en fejl, hvis stien ikke kan findes.

Standardværdien er lax , så hvis du ikke angiver en stitilstand, lax tilstand vil blive brugt.

Her er nogle eksempler for at demonstrere, hvad der sker med hver tilstand, når stien ikke kan findes.

Andet argument

I de næste to eksempler angiver jeg en ikke-eksisterende sti i det andet argument, når jeg kalder OPENJSON() . Det første eksempel viser, hvad der sker, når du bruger slap tilstand, det andet eksempel viser, hvad der sker, når du bruger streng tilstand.

Laks tilstand

Her er, hvad der sker i lax tilstand, når stien ikke kan findes.

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');

Resultat:

(0 rows affected)

Ingen fejl. Kun nul resultater returneret.

Strikt tilstand

Her er den nu i strict mode.

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');

Resultat:

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

Som forventet resulterede streng tilstand i en fejl.

I WITH-klausulen

I de næste to eksempler tester vi igen slap tilstand vs streng tilstand, bortset fra at denne gang angiver vi det i WITH klausul, når skemaet defineres.

Laks tilstand

Her er, hvad der sker i lax tilstand.

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   
    );

Resultat:

+----------+------------+--------+------------------------------------------------------+
| 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" }     |
+----------+------------+--------+------------------------------------------------------+

I dette tilfælde bruger jeg 'lax $.born' fordi jeg forsøger at henvise til en nøgle kaldet born , men sådan en nøgle findes ikke i JSON.

Denne gang resulterer den kolonne, der ikke kan findes, i en NULL værdi.

Strikt tilstand

Her er den nu i strict tilstand.

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   
    );

Resultat:

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

Denne gang brugte jeg 'strict $.born' .

Som forventet resulterede streng tilstand i en fejl.

Kompatibilitetsniveau

OPENJSON() funktion er kun tilgængelig under kompatibilitetsniveau 130 eller højere.

Hvis dit databasekompatibilitetsniveau er lavere end 130, vil SQL Server ikke være i stand til at finde og køre OPENJSON() , og du får en fejl.

Du kan kontrollere dit databasekompatibilitetsniveau via sys.databases katalogvisning.

Du kan ændre dets kompatibilitetsniveau sådan her:

ALTER DATABASE DatabaseName 
SET COMPATIBILITY_LEVEL = 150;

Ny til JSON?

Hvis du ikke er så fortrolig med JSON, så tjek min JSON-tutorial på Quackit.


  1. En praktisk brug af SQL COALESCE-funktionen

  2. Kan jeg få en plpgsql-funktion til at returnere et heltal uden at bruge en variabel?

  3. SQL-forespørgsel for at få den seneste række for hver forekomst af en given nøgle

  4. Opret en SQL Server-database med SQLOPS