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 enNULL
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.