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

Come selezionare JSON annidato in SQL Server con OPENJSON

Se stai usando OPENJSON() , ma stai cercando di ricordare come selezionare un frammento interno dal documento JSON, continua a leggere.

Il OPENJSON() la sintassi ti consente di convertire i documenti JSON in una vista tabulare. Consente inoltre di selezionare un frammento JSON nidificato dal documento JSON.

Il modo per farlo è con percorsi .

Percorsi

Un percorso è composto da:

  • Un simbolo del dollaro ($ ), che rappresenta l'elemento di contesto.
  • Un insieme di passaggi del percorso. I passaggi del percorso possono contenere i seguenti elementi e operatori:
    • Nomi delle chiavi. Ad esempio, $.pets e $.pets.dogs . Se il nome della chiave inizia con il simbolo del dollaro o contiene caratteri speciali come spazi, deve essere racchiuso tra virgolette (ad esempio $."my pets" ).
    • Elementi della matrice. Ad esempio, $.pets.dogs[1] . Gli indici di array sono a base zero, quindi questo esempio seleziona il secondo elemento nell'array.
    • L'operatore punto (. ) indica un membro di un oggetto. Ad esempio, in $.pets.dogs , dogs è un membro di pets .

Esempio di base

Ecco un semplice esempio da dimostrare.

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" : [
            { "id" : 1, "name" : "Fetch", "sex" : "Male" },
            { "id" : 2, "name" : "Fluffy", "sex" : "Male" },
            { "id" : 3, "name" : "Wag", "sex" : "Female" }
        ]
    }
}';

SELECT *
FROM OPENJSON(@json, '$.pets.dogs')
WITH  (
        [id]    int,  
        [name]  varchar(60), 
        [sex]   varchar(6)
    );

Risultato:

+------+--------+--------+
| id   | name   | sex    |
|------+--------+--------|
| 1    | Fetch  | Male   |
| 2    | Fluffy | Male   |
| 3    | Wag    | Female |
+------+--------+--------+

In questo caso, il secondo argomento di OPENJSON() è '$.pets.dogs' , il che significa che stiamo selezionando il valore dei dogs chiave, che a sua volta è figlia di pets .

Il simbolo del dollaro ($ ) rappresenta l'elemento di contesto.

Nota che in questo esempio utilizzo anche WITH clausola per definire lo schema. Se non lo includessi, verrebbe invece utilizzato lo schema predefinito.

Ecco come appare usando lo schema predefinito.

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" : [
            { "id" : 1, "name" : "Fetch", "sex" : "Male" },
            { "id" : 2, "name" : "Fluffy", "sex" : "Male" },
            { "id" : 3, "name" : "Wag", "sex" : "Female" }
        ]
    }
}';

SELECT *
FROM OPENJSON(@json, '$.pets.dogs');

Risultato:

+-------+-------------------------------------------------+--------+
| key   | value                                           | type   |
|-------+-------------------------------------------------+--------|
| 0     | { "id" : 1, "name" : "Fetch", "sex" : "Male" }  | 5      |
| 1     | { "id" : 2, "name" : "Fluffy", "sex" : "Male" } | 5      |
| 2     | { "id" : 3, "name" : "Wag", "sex" : "Female" }  | 5      |
+-------+-------------------------------------------------+--------+

Quindi stiamo ancora selezionando lo stesso JSON annidato, è solo che stiamo usando uno schema diverso.

Lo schema predefinito restituisce sempre tre colonne; chiave , valore e digita .

Selezione degli elementi dell'array

Come accennato, puoi usare la notazione con parentesi quadre per selezionare un elemento specifico in un array.

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" : [
            { "id" : 1, "name" : "Fetch", "sex" : "Male" },
            { "id" : 2, "name" : "Fluffy", "sex" : "Male" },
            { "id" : 3, "name" : "Wag", "sex" : "Female" }
        ]
    }
}';

SELECT *
FROM OPENJSON(@json, '$.pets.dogs[0]')
WITH  (
        [id]    int,  
        [name]  varchar(60), 
        [sex]   varchar(6)
    );

Risultato:

+------+--------+-------+
| id   | name   | sex   |
|------+--------+-------|
| 1    | Fetch  | Male  |
+------+--------+-------+

Dato che gli indici di matrice sono a base zero, specificando un valore di 0 restituisce il primo elemento dell'array.

Ecco come appare questo esempio quando si utilizza lo schema predefinito (cioè senza il WITH clausola).

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" : [
            { "id" : 1, "name" : "Fetch", "sex" : "Male" },
            { "id" : 2, "name" : "Fluffy", "sex" : "Male" },
            { "id" : 3, "name" : "Wag", "sex" : "Female" }
        ]
    }
}';

SELECT *
FROM OPENJSON(@json, '$.pets.dogs[0]');

Risultato:

+-------+---------+--------+
| key   | value   | type   |
|-------+---------+--------|
| id    | 1       | 2      |
| name  | Fetch   | 1      |
| sex   | Male    | 1      |
+-------+---------+--------+

Modalità percorso

Quando si utilizzano percorsi, è possibile dichiarare la modalità percorso.

La modalità percorso determina cosa succede quando un'espressione percorso contiene un errore.

La modalità del percorso può essere lax o strict .

  • In lax modalità, la funzione restituisce valori vuoti se non è possibile trovare il percorso. Ad esempio, se richiedi il valore $.pets.cows , ma il JSON non contiene quella chiave, la funzione restituisce null, ma non genera un errore.
  • In strict modalità, la funzione genera un errore se non è possibile trovare il percorso.

La modalità del percorso predefinita è lax , quindi se non lo dichiari, lax viene utilizzato.

Esempio

Ecco un esempio per dimostrare come ciascuna modalità di percorso gestisce i percorsi mancanti.

Modalità negligente

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" : [
            { "id" : 1, "name" : "Fetch", "sex" : "Male" },
            { "id" : 2, "name" : "Fluffy", "sex" : "Male" },
            { "id" : 3, "name" : "Wag", "sex" : "Female" }
        ]
    }
}';

SELECT *
FROM OPENJSON(@json, 'lax $.pets.cows');

Risultato:

(0 rows affected)

Quindi la modalità lassista non ha generato errori. Ciò ha semplicemente comportato l'effetto di zero righe.

Se abbiamo specificato il nostro schema e abbiamo selezionato il sottooggetto corretto, ma abbiamo utilizzato un percorso mancante per mappare il nome di una colonna, restituirebbe NULL in quella colonna.

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" : [
            { "id" : 1, "name" : "Fetch", "sex" : "Male" },
            { "id" : 2, "name" : "Fluffy", "sex" : "Male" },
            { "id" : 3, "name" : "Wag", "sex" : "Female" }
        ]
    }
}'

SELECT *
FROM OPENJSON(@json, 'lax $.pets.dogs')
WITH  (
        [id]    int         'lax $.id',  
        [name]  varchar(60) 'lax $.name', 
        [color]   varchar(6)  'lax $.color'
    );

Risultato:

+------+--------+---------+
| id   | name   | color   |
|------+--------+---------|
| 1    | Fetch  | NULL    |
| 2    | Fluffy | NULL    |
| 3    | Wag    | NULL    |
+------+--------+---------+

Modalità rigorosa

Ecco cosa succede quando utilizziamo la modalità rigorosa.

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" : [
            { "id" : 1, "name" : "Fetch", "sex" : "Male" },
            { "id" : 2, "name" : "Fluffy", "sex" : "Male" },
            { "id" : 3, "name" : "Wag", "sex" : "Female" }
        ]
    }
}';

SELECT *
FROM OPENJSON(@json, 'strict $.pets.cows');

Risultato:

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

Come previsto, si è verificato un errore.

Lo stesso errore si verifica quando selezioniamo la chiave JSON corretta, ma mappiamo una colonna a una chiave inesistente.

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" : [
            { "id" : 1, "name" : "Fetch", "sex" : "Male" },
            { "id" : 2, "name" : "Fluffy", "sex" : "Male" },
            { "id" : 3, "name" : "Wag", "sex" : "Female" }
        ]
    }
}';

SELECT *
FROM OPENJSON(@json, 'strict $.pets.dogs')
WITH  (
        [id]    int         'strict $.id',  
        [name]  varchar(60) 'strict $.name', 
        [color]   varchar(6)  'strict $.color'
    );

Risultato:

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

Percorsi duplicati

Se il tuo documento JSON contiene percorsi duplicati allo stesso livello di nidificazione, OPENJSON() può restituirli tutti.

Questo è in contrasto con JSON_VALUE() e JSON_QUERY() , entrambi restituiscono solo il primo valore che corrisponde al percorso.

Ecco un esempio di utilizzo di OPENJSON() per restituire percorsi duplicati.

DECLARE @json NVARCHAR(4000) = N'{
    "dog": {
            "names": {
                "name": "Fetch", 
                "name": "Good Dog"
            }
        }
    }';
SELECT * FROM OPENJSON(@json, '$.dog.names');

Risultato:

+-------+----------+--------+
| key   | value    | type   |
|-------+----------+--------|
| name  | Fetch    | 1      |
| name  | Good Dog | 1      |
+-------+----------+--------+

Oggetti secondari nidificati

Quando definisci il tuo schema, puoi utilizzare AS JSON opzione per restituire un intero sottooggetto come proprio documento JSON.

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" : [
            { "id" : 1, "name" : "Fetch", "sex" : "Male" },
            { "id" : 2, "name" : "Fluffy", "sex" : "Male" },
            { "id" : 3, "name" : "Wag", "sex" : "Female" }
        ]
    }
}';

SELECT *
FROM OPENJSON(@json, '$.pets')
WITH  (
        [dogs]  nvarchar(max) '$.dogs' AS JSON
    );

Risultato:

+--------+
| dogs   |
|--------|
| [
            { "id" : 1, "name" : "Fetch", "sex" : "Male" },
            { "id" : 2, "name" : "Fluffy", "sex" : "Male" },
            { "id" : 3, "name" : "Wag", "sex" : "Female" }
        ]        |
+--------+

Se non avessimo usato AS JSON opzione, avremmo ricevuto un errore o NULL, a seconda che avessimo specificato lax o strict modalità.

Eccolo in ogni modalità quando si omette AS JSON opzione.

Modalità rilassata

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" : [
            { "id" : 1, "name" : "Fetch", "sex" : "Male" },
            { "id" : 2, "name" : "Fluffy", "sex" : "Male" },
            { "id" : 3, "name" : "Wag", "sex" : "Female" }
        ]
    }
}';

SELECT *
FROM OPENJSON(@json, '$.pets')
WITH  (
        [dogs]  nvarchar(max) 'lax $.dogs'
    );

Risultato:

+--------+
| dogs   |
|--------|
| NULL   |
+--------+

Modalità rigorosa

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" : [
            { "id" : 1, "name" : "Fetch", "sex" : "Male" },
            { "id" : 2, "name" : "Fluffy", "sex" : "Male" },
            { "id" : 3, "name" : "Wag", "sex" : "Female" }
        ]
    }
}';

SELECT *
FROM OPENJSON(@json, '$.pets')
WITH  (
        [dogs]  nvarchar(max) 'strict $.dogs'
    );

Risultato:

Msg 13624, Level 16, State 1, Line 16
Object or array cannot be found in the specified JSON path.