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

Introduzione a OPENJSON con esempi (SQL Server)

SQL Server dispone di una funzione con valori di tabella denominata OPENJSON() che crea una vista relazionale dei dati JSON.

Quando lo chiami, passi un documento JSON come argomento e OPENJSON() quindi lo analizza e restituisce gli oggetti e le proprietà del documento JSON in un formato tabulare, come righe e colonne.

Esempio

Ecco un semplice esempio da dimostrare.

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

Risultato:

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

Per impostazione predefinita, OPENJSON() restituisce una tabella con tre colonne; chiave , valore e digita .

Hai anche la possibilità di specificare il tuo schema (il che significa che puoi definire le tue colonne). Nel mio semplice esempio, ho utilizzato lo schema predefinito e quindi sono state restituite le tre colonne predefinite.

Queste colonne sono definite come segue:

Colonna Descrizione
chiave Contiene il nome della proprietà specificata o l'indice dell'elemento nell'array specificato. Questo è un nvarchar(4000) valore e la colonna ha regole di confronto BIN2.
valore Contiene il valore della proprietà. Questo è un nvarchar(max) value e la colonna eredita le regole di confronto dal JSON fornito.
tipo Contiene il tipo JSON del valore. Questo è rappresentato come un int valore (da 0 a 5 ). Questa colonna viene restituita solo quando si utilizza lo schema predefinito.

Tipi predefiniti

Nel mondo di JSON, ci sono sei tipi di dati. Questi sono stringa , numero , vero/falso (booleano), null , oggetto e array .

Quando analizzi alcuni JSON tramite OPENJSON() utilizzando lo schema predefinito, OPENJSON() determina qual è il tipo JSON, quindi compila il tipo colonna con un int valore che rappresenta quel tipo.

Il int il valore può quindi variare da 0 a 5 . Ogni int value rappresenta un tipo JSON come indicato nella tabella seguente.

Valore nella colonna "tipo" Tipo di dati JSON
0 nullo
1 stringa
2 numero
3 vero/falso
4 matrice
5 oggetto

L'esempio seguente restituisce tutti e sei questi tipi 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}]');

Risultato:

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

Restituisci JSON nidificato

Puoi restituire un oggetto o una matrice nidificata specificandone il percorso come secondo argomento facoltativo.

In altre parole, non devi analizzare l'intero documento JSON:puoi scegliere di analizzare solo la parte che ti interessa.

Ecco un esempio.

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

Risultato:

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

In questo caso, ho specificato un percorso di $.pets.cats , che ha prodotto solo il valore di gatti essere restituito. Il valore dei gatti è un array, quindi è stato restituito l'intero array.

Per restituire un solo gatto (cioè un elemento dell'array), possiamo usare la sintassi delle parentesi quadre per restituire i valori dell'array (come questo $.pets.cats[1] ).

Ecco lo stesso esempio modificato per restituire un solo elemento dell'array:

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

Risultato:

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

Gli indici di array JSON sono in base zero, quindi questo esempio ha restituito il secondo valore di array (perché ho specificato $.pets.cats[1] ).

Se avessi specificato $.pets.cats[0] , sarebbe stato restituito il primo valore (ovvero il gatto chiamato "Fluffy").

Definisci uno schema

Come accennato, puoi specificare il tuo schema (cioè definire le tue colonne e tipi).

Ecco un esempio di come farlo.

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

Risultato:

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

Possiamo vedere che i nomi delle colonne riflettono quelli che ho specificato nel WITH clausola. In quella clausola, ho mappato ogni chiave JSON sui miei nomi di colonna preferiti. Ho anche specificato il tipo di dati di SQL Server che voglio per ogni colonna.

Ho anche usato AS JSON sull'ultima colonna per restituire quella colonna come un frammento JSON. Quando utilizzi AS JSON, il tipo di dati deve essere nvarchar(max) .

Verifica i tipi di dati

Possiamo utilizzare la query seguente per verificare i tipi di dati di ciascuna colonna.

Questa query utilizza il sys.dm_exec_describe_first_result_set vista a gestione dinamica del sistema, che restituisce i metadati sul primo set di risultati da una query.

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

Risultato:

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

Possiamo vedere che corrispondono perfettamente al mio schema.

Tieni presente che la chiave , valore e digita le colonne non sono disponibili quando si definisce il proprio schema. Tali colonne sono disponibili solo quando si utilizza lo schema predefinito.

Inserisci il JSON analizzato in una tabella

A questo punto potresti pensare che potremmo facilmente inserire il nostro JSON analizzato in una tabella di database.

E avresti ragione.

L'abbiamo già preparato con colonne e righe e abbiamo persino assegnato un nome alle colonne e assegnato loro tipi di dati.

Ora è il momento di inserirlo in una tabella.

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

Tutto quello che ho fatto è stato aggiungere INTO JsonCats alla mia richiesta, per creare una tabella chiamata JsonCats e inserisci i risultati della query al suo interno.

Ora selezioniamo il contenuto di quella tabella.

SELECT * FROM JsonCats;

Risultato:

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

I contenuti sono esattamente come li abbiamo visti nell'esempio precedente.

E solo per essere assolutamente sicuri, ora possiamo usare sys.column la visualizzazione del catalogo di sistema controlla i nomi e i tipi delle colonne della tabella.

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

Risultato:

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

Ancora una volta, esattamente come l'avevamo specificato.

Nota che sys.columns restituisce sempre un max_length di -1 quando il tipo di dati della colonna è varchar(max) , nvarchar(max) , variabile(max) o xml . Abbiamo specificato nvarchar(max) e quindi il valore di -1 è esattamente come previsto.

Modalità Percorso:Lassista vs Severo

Il percorso fornito nel secondo argomento o in WITH la clausola può (facoltativamente) iniziare con lax o strict parola chiave.

  • In lax modalità, OPENJSON() non genera un errore se non è possibile trovare l'oggetto o il valore nel percorso specificato. Se non è possibile trovare il percorso, OPENJSON() restituisce un set di risultati vuoto o un NULL valore.
  • In strict modalità, OPENJSON() restituisce un errore se non è possibile trovare il percorso.

Il valore predefinito è lax , quindi se non specifichi una modalità di percorso, lax verrà utilizzata la modalità.

Ecco alcuni esempi per dimostrare cosa succede con ciascuna modalità quando non è possibile trovare il percorso.

Secondo argomento

Nei prossimi due esempi, fornisco un percorso inesistente nel secondo argomento quando chiamo OPENJSON() . Il primo esempio mostra cosa succede quando si usa la modalità lax, il secondo esempio mostra cosa succede quando si usa la modalità strict.

Modalità negligente

Ecco cosa succede in lax modalità quando non è possibile trovare il percorso.

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

Risultato:

(0 rows affected)

Nessun errore. Sono stati restituiti solo zero risultati.

Modalità rigorosa

Ora eccolo in strict modalità.

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

Risultato:

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

Come previsto, la modalità rigorosa ha generato un errore.

Nella clausola WITH

Nei prossimi due esempi, testiamo di nuovo la modalità lax rispetto alla modalità rigorosa, tranne che questa volta lo specifichiamo nel WITH clausola durante la definizione dello schema.

Modalità negligente

Ecco cosa succede in lax modalità.

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

Risultato:

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

In questo caso utilizzo 'lax $.born' perché sto cercando di fare riferimento a una chiave chiamata born , ma tale chiave non esiste nel JSON.

Questa volta la colonna che non può essere trovata restituisce un NULL valore.

Modalità rigorosa

Ora eccolo in strict modalità.

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

Risultato:

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

Questa volta ho usato 'strict $.born' .

Come previsto, la modalità rigorosa ha generato un errore.

Livello di compatibilità

Il OPENJSON() la funzione è disponibile solo con il livello di compatibilità 130 o superiore.

Se il livello di compatibilità del database è inferiore a 130, SQL Server non sarà in grado di trovare ed eseguire OPENJSON() e riceverai un errore.

Puoi controllare il livello di compatibilità del tuo database tramite sys.databases vista catalogo.

Puoi cambiarne il livello di compatibilità in questo modo:

ALTER DATABASE DatabaseName 
SET COMPATIBILITY_LEVEL = 150;

Nuovo JSON?

Se non hai molta familiarità con JSON, dai un'occhiata al mio tutorial JSON su Quackit.