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