sql >> Databasteknik >  >> RDS >> Sqlserver

Introduktion till OPENJSON med exempel (SQL-server)

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 en NULL 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.


  1. SQL-punktnotation

  2. Flera index vs index med flera kolumner

  3. Kalkylblad kontra databaser:Är det dags att byta? Del 2

  4. Hur deklarerar och använder jag variabler i PL/SQL som jag gör i T-SQL?