SQL Server har en tabellvärderad funktion som heter OPENJSON()
som skapar en relationsvy av JSON-data.
När du anropar det skickar du ett JSON-dokument som ett argument och OPENJSON()
analyserar det sedan och returnerar JSON-dokumentets objekt och egenskaper i ett tabellformat – som rader och kolumner.
Exempel
Här är ett enkelt exempel att visa.
SELECT * FROM OPENJSON('["Cat","Dog","Bird"]');
Resultat:
+-------+---------+--------+ | key | value | type | |-------+---------+--------| | 0 | Cat | 1 | | 1 | Dog | 1 | | 2 | Bird | 1 | +-------+---------+--------+
Som standard är OPENJSON()
returnerar en tabell med tre kolumner; nyckel , värde och skriv .
Du har också möjlighet att ange ditt eget schema (vilket innebär att du kan definiera dina egna kolumner). I mitt enkla exempel använde jag standardschemat, så de tre standardkolumnerna returnerades.
Dessa kolumner definieras enligt följande:
Kolumn | Beskrivning |
---|---|
nyckel | Innehåller namnet på den angivna egenskapen eller indexet för elementet i den angivna arrayen. Det här är en nvarchar(4000) värde och kolumnen har en BIN2-sortering. |
värde | Innehåller fastighetens värde. Detta är en nvarchar(max) värde, och kolumnen ärver sin sortering från den angivna JSON. |
typ | Innehåller JSON-typen för värdet. Detta representeras som en int värde (från 0 till 5 ). Den här kolumnen returneras endast när du använder standardschemat. |
Standardtyper
I JSON-världen finns det sex datatyper. Dessa är strängar , nummer , sant/falskt (boolesk), null , objekt och array .
När du tolkar en del JSON genom OPENJSON()
med standardschemat, OPENJSON()
räknar ut vad JSON-typen är och fyller sedan i typen kolumn med en int värde som representerar den typen.
int värdet kan därför variera från 0
till 5
. Varje int värde representerar en JSON-typ som beskrivs i följande tabell.
Värde i kolumnen "typ" | JSON-datatyp |
---|---|
0 | null |
1 | sträng |
2 | nummer |
3 | sant/falskt |
4 | array |
5 | objekt |
Följande exempel returnerar alla sex av dessa 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)
Returnera kapslad JSON
Du kan returnera ett kapslat objekt eller array genom att ange dess sökväg som ett valfritt andra argument.
Med andra ord behöver du inte analysera hela JSON-dokumentet – du kan välja att analysera bara den del du är intresserad av.
Här är ett exempel.
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 det här fallet angav jag en sökväg för $.pets.cats
, vilket endast resulterade i värdet av katter återlämnas. Värdet på katter är en array, så hela arrayen returnerades.
För att returnera bara en katt (d.v.s. ett arrayelement) kan vi använda hakparentessyntaxen för att returnera arrayvärden (som denna $.pets.cats[1]
).
Här är samma exempel modifierat för att returnera bara ett matriselement:
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-arrayindex är nollbaserade, så det här exemplet returnerade det andra arrayvärdet (eftersom jag angav $.pets.cats[1]
).
Om jag hade angett $.pets.cats[0]
, skulle det första värdet ha returnerats (d.v.s. katten som heter "Fluffy").
Definiera ett schema
Som nämnts kan du specificera ditt eget schema (dvs definiera dina egna kolumner och typer).
Här är ett exempel på hur du gör 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 att kolumnnamnen återspeglar de som jag angav i WITH
klausul. I den klausulen mappade jag varje JSON-nyckel till mina egna föredragna kolumnnamn. Jag angav också vilken SQL Server-datatyp jag vill ha för varje kolumn.
Jag använde också AS JSON
i den sista kolumnen för att returnera den kolumnen som ett JSON-fragment. När du använder AS JSON måste datatypen vara nvarchar(max) .
Verifiera datatyperna
Vi kan använda följande fråga för att verifiera datatyperna för varje kolumn.
Den här frågan använder sys.dm_exec_describe_first_result_set
system dynamisk hanteringsvy, som returnerar metadata om den första resultatuppsättningen från en fråga.
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 att de matchar mitt schema perfekt.
Observera att nyckeln , värde och skriv kolumner är inte tillgängliga när du definierar ditt eget schema. Dessa kolumner är endast tillgängliga när du använder standardschemat.
Infoga den analyserade JSON-filen i en tabell
Vid det här laget kanske du tänker att vi enkelt skulle kunna infoga vår analyserade JSON i en databastabell.
Och du har rätt.
Vi har redan förberett det med kolumner och rader, och vi har till och med döpt kolumnerna och gett dem datatyper.
Nu är det dags att infoga det i en tabell.
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
);
Allt jag gjorde var att lägga till INTO JsonCats
till min fråga, för att skapa en tabell som heter JsonCats
och infoga resultatet av frågan i den.
Låt oss nu välja innehållet i den tabellen.
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" } | +----------+------------+--------+------------------------------------------------------+
Innehållet är exakt som vi såg det i det tidigare exemplet.
Och bara för att vara helt säker kan vi nu använda sys.column
systemkatalogvy kontrollera tabellens kolumnnamn och 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 | +----------+----------+--------------+
Återigen, exakt hur vi hade specificerat det.
Observera att sys.columns
returnerar alltid en max_length
av -1
när kolumndatatypen är varchar(max) , nvarchar(max) , varbinary(max) eller xml . Vi angav nvarchar(max) och så värdet på -1
är precis som förväntat.
Path Mode:Lax vs Strict
Sökvägen som anges i det andra argumentet eller i WITH
sats kan (valfritt) börja med lax
eller strict
nyckelord.
- I
lax
läge,OPENJSON()
ger inte upp ett fel om objektet eller värdet på den angivna sökvägen inte kan hittas. Om sökvägen inte kan hittas,OPENJSON()
returnerar antingen en tom resultatuppsättning eller enNULL
värde. - I
strict
läge,OPENJSON()
returnerar ett fel om sökvägen inte kan hittas.
Standardvärdet är lax
, så om du inte anger ett sökvägsläge, lax
läget kommer att användas.
Här är några exempel för att visa vad som händer med varje läge när sökvägen inte kan hittas.
Andra argumentet
I de följande två exemplen tillhandahåller jag en icke-existerande sökväg i det andra argumentet när jag anropar OPENJSON()
. Det första exemplet visar vad som händer när man använder slappt läge, det andra exemplet visar vad som händer när man använder strikt läge.
Laxläge
Här är vad som händer i lax
läge när sökvägen inte kan hittas.
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)
Inget fel. Bara noll resultat returnerades.
Strikt läge
Nu är den här i strict
läge.
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 väntat resulterade strikt läge i ett fel.
I WITH-klausulen
I de följande två exemplen testar vi återigen slappt läge kontra strikt läge, förutom att vi den här gången specificerar det i WITH
klausul när du definierar schemat.
Laxläge
Här är vad som händer i lax
läge.
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 det här fallet använder jag 'lax $.born'
eftersom jag försöker referera till en nyckel som heter born
, men en sådan nyckel finns inte i JSON.
Den här gången resulterar kolumnen som inte kan hittas i en NULL
värde.
Strikt läge
Nu är den här i strict
läge.
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.
Den här gången använde jag 'strict $.born'
.
Som väntat resulterade strikt läge i ett fel.
Kompatibilitetsnivå
OPENJSON()
Funktionen är endast tillgänglig under kompatibilitetsnivå 130 eller högre.
Om din databaskompatibilitetsnivå är lägre än 130 kommer SQL Server inte att kunna hitta och köra OPENJSON()
, och du får ett felmeddelande.
Du kan kontrollera din databaskompatibilitetsnivå via sys.databases
katalogvy.
Du kan ändra dess kompatibilitetsnivå så här:
ALTER DATABASE DatabaseName
SET COMPATIBILITY_LEVEL = 150;
Ny på JSON?
Om du inte är så bekant med JSON, kolla in min JSON-handledning på Quackit.