Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Comment sélectionner JSON imbriqué dans SQL Server avec OPENJSON

Si vous utilisez OPENJSON() , mais vous essayez de vous rappeler comment sélectionner un fragment interne à partir du document JSON, lisez la suite.

Le OPENJSON() La syntaxe vous permet de convertir des documents JSON en vue tabulaire. Il vous permet également de sélectionner un fragment JSON imbriqué dans le document JSON.

Pour ce faire, utilisez les chemins .

Chemins

Un chemin se compose des éléments suivants :

  • Un signe dollar ($ ), qui représente l'élément de contexte.
  • Un ensemble d'étapes de parcours. Les étapes du chemin peuvent contenir les éléments et opérateurs suivants :
    • Noms clés. Par exemple, $.pets et $.pets.dogs . Si le nom de la clé commence par un signe dollar ou contient des caractères spéciaux tels que des espaces, il doit être entouré de guillemets (par exemple $."my pets" ).
    • Éléments de tableau. Par exemple, $.pets.dogs[1] . Les index de tableau sont basés sur zéro, donc cet exemple sélectionne le deuxième élément du tableau.
    • L'opérateur point (. ) indique un membre d'un objet. Par exemple, dans $.pets.dogs , dogs est membre de pets .

Exemple de base

Voici un exemple simple pour illustrer.

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

Résultat :

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

Dans ce cas, le deuxième argument de OPENJSON() est '$.pets.dogs' , ce qui signifie que nous sélectionnons la valeur des dogs key, qui est elle-même un enfant de pets .

Le signe dollar ($ ) représente l'élément de contexte.

Notez que dans cet exemple, j'utilise également le WITH clause pour définir le schéma. Si je n'incluais pas cela, le schéma par défaut serait utilisé à la place.

Voici à quoi cela ressemble en utilisant le schéma par défaut.

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

Résultat :

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

Nous sélectionnons donc toujours le même JSON imbriqué, c'est juste que nous utilisons un schéma différent.

Le schéma par défaut renvoie toujours trois colonnes ; clé , valeur , et tapez .

Sélectionner les éléments du tableau

Comme mentionné, vous pouvez utiliser la notation entre crochets pour sélectionner un élément spécifique dans un tableau.

Voici un exemple.

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

Résultat :

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

Étant donné que les index de tableau sont basés sur zéro, en spécifiant une valeur de 0 renvoie le premier élément du tableau.

Voici à quoi ressemble cet exemple lorsque vous utilisez le schéma par défaut (c'est-à-dire sans le WITH clause).

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

Résultat :

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

Mode Chemin

Lorsque vous utilisez des chemins, vous avez la possibilité de déclarer le mode chemin.

Le mode chemin détermine ce qui se passe lorsqu'une expression de chemin contient une erreur.

Le mode de chemin peut être soit lax ou strict .

  • En lax mode, la fonction renvoie des valeurs vides si le chemin est introuvable. Par exemple, si vous demandez la valeur $.pets.cows , mais le JSON ne contient pas cette clé, la fonction renvoie null, mais ne génère pas d'erreur.
  • En strict mode, la fonction génère une erreur si le chemin est introuvable.

Le mode de chemin par défaut est lax , donc si vous ne le déclarez pas, lax est utilisé.

Exemple

Voici un exemple pour montrer comment chaque mode de chemin gère les chemins manquants.

Mode laxiste

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

Résultat :

(0 rows affected)

Donc, le mode laxiste n'a généré aucune erreur. Il en résultait simplement qu'aucune ligne n'était affectée.

Si nous spécifions notre propre schéma et que nous sélectionnions le sous-objet correct, mais que nous utilisions un chemin manquant pour mapper à un nom de colonne, il renverrait NULL dans cette colonne.

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

Résultat :

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

Mode strict

Voici ce qui se passe lorsque nous utilisons le mode strict.

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

Résultat :

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

Comme prévu, cela a généré une erreur.

La même erreur se produit lorsque nous sélectionnons la bonne clé JSON, mais mappons une colonne à une clé inexistante.

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

Résultat :

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

Chemins en double

Si votre document JSON contient des chemins en double au même niveau d'imbrication, OPENJSON() peut tous les retourner.

Ceci est en contraste avec JSON_VALUE() et JSON_QUERY() , qui renvoient tous deux uniquement la première valeur correspondant au chemin.

Voici un exemple d'utilisation de OPENJSON() pour renvoyer des chemins en double.

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

Résultat :

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

Sous-objets imbriqués

Lors de la définition de votre propre schéma, vous pouvez utiliser le AS JSON option pour renvoyer un sous-objet entier en tant que son propre document 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
    );

Résultat :

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

Si nous n'avions pas utilisé le AS JSON option, nous aurions reçu une erreur ou NULL, selon si nous avions spécifié lax ou strict mode.

Le voici dans chaque mode en omettant le AS JSON option.

Mode laxiste

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

Résultat :

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

Mode strict

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

Résultat :

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