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

Introduction à OPENJSON avec exemples (SQL Server)

SQL Server a une fonction table appelée OPENJSON() qui crée une vue relationnelle des données JSON.

Lorsque vous l'appelez, vous passez un document JSON en argument, et OPENJSON() puis l'analyse et renvoie les objets et les propriétés du document JSON dans un format tabulaire - sous forme de lignes et de colonnes.

Exemple

Voici un exemple simple pour illustrer.

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

Résultat :

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

Par défaut, OPENJSON() renvoie un tableau à trois colonnes ; clé , valeur , et tapez .

Vous avez également la possibilité de spécifier votre propre schéma (ce qui signifie que vous pouvez définir vos propres colonnes). Dans mon exemple simple, j'ai utilisé le schéma par défaut, et donc les trois colonnes par défaut ont été renvoyées.

Ces colonnes sont définies comme suit :

Colonne Description
clé Contient le nom de la propriété spécifiée ou l'index de l'élément dans le tableau spécifié. Ceci est un nvarchar(4000) valeur, et la colonne a un classement BIN2.
valeur Contient la valeur de la propriété. Ceci est un nvarchar(max) valeur, et la colonne hérite son classement du JSON fourni.
type Contient le type JSON de la valeur. Ceci est représenté par un int valeur (de 0 à 5 ). Cette colonne n'est renvoyée que lorsque vous utilisez le schéma par défaut.

Types par défaut

Dans le monde de JSON, il existe six types de données. Ce sont des chaînes , nombre , vrai/faux (booléen), null , objet , et tableau .

Lorsque vous analysez du JSON via OPENJSON() en utilisant le schéma par défaut, OPENJSON() détermine quel est le type JSON, puis remplit le type colonne avec un int valeur qui représente ce type.

Le int la valeur peut donc aller de 0 à 5 . Chaque int value représente un type JSON comme indiqué dans le tableau suivant.

Valeur dans la colonne "type" Type de données JSON
0 null
1 chaîne
2 numéro
3 vrai/faux
4 tableau
5 objet

L'exemple suivant renvoie ces six types 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}]');

Résultat :

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

Renvoyer JSON imbriqué

Vous pouvez renvoyer un objet ou un tableau imbriqué en spécifiant son chemin comme deuxième argument facultatif.

En d'autres termes, vous n'avez pas à analyser l'intégralité du document JSON. Vous pouvez choisir d'analyser uniquement la partie qui vous intéresse.

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" : [
            { "name" : "Fetch", "sex" : "Male" },
            { "name" : "Fluffy", "sex" : "Male" },
            { "name" : "Wag", "sex" : "Female" }
        ]
    }
}';
SELECT * FROM OPENJSON(@json, '$.pets.cats');

Résultat :

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

Dans ce cas, j'ai spécifié un chemin de $.pets.cats , ce qui n'a donné que la valeur de cats étant retourné. La valeur des chats est un tableau, donc tout le tableau a été renvoyé.

Pour renvoyer un seul chat (c'est-à-dire un élément de tableau), nous pouvons utiliser la syntaxe des crochets pour renvoyer les valeurs du tableau (comme ceci $.pets.cats[1] ).

Voici le même exemple modifié pour renvoyer un seul élément de tableau :

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

Résultat :

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

Les index de tableau JSON sont basés sur zéro, donc cet exemple a renvoyé la deuxième valeur de tableau (parce que j'ai spécifié $.pets.cats[1] ).

Si j'avais spécifié $.pets.cats[0] , la première valeur aurait été renvoyée (c'est-à-dire le chat nommé "Fluffy").

Définir un schéma

Comme mentionné, vous pouvez spécifier votre propre schéma (c'est-à-dire définir vos propres colonnes et types).

Voici un exemple de cela.

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

Résultat :

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

Nous pouvons voir que les noms de colonnes reflètent ceux que j'ai spécifiés dans le WITH clause. Dans cette clause, j'ai mappé chaque clé JSON sur mes propres noms de colonne préférés. J'ai également spécifié le type de données SQL Server que je veux pour chaque colonne.

J'ai aussi utilisé AS JSON sur la dernière colonne pour renvoyer cette colonne en tant que fragment JSON. Lorsque vous utilisez AS JSON, le type de données doit être nvarchar(max) .

Vérifier les types de données

Nous pouvons utiliser la requête suivante pour vérifier les types de données de chaque colonne.

Cette requête utilise le sys.dm_exec_describe_first_result_set vue de gestion dynamique du système, qui renvoie des métadonnées sur le premier ensemble de résultats d'une requête.

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

Résultat :

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

On voit qu'ils correspondent parfaitement à mon schéma.

Notez que la clé , valeur , et tapez les colonnes ne sont pas disponibles lorsque vous définissez votre propre schéma. Ces colonnes ne sont disponibles que lors de l'utilisation du schéma par défaut.

Insérer le JSON analysé dans une table

À présent, vous pensez peut-être que nous pourrions facilement insérer notre JSON analysé dans une table de base de données.

Et vous auriez raison.

Nous l'avons déjà préparé avec des colonnes et des lignes, et nous avons même nommé les colonnes et leur avons donné des types de données.

Il est maintenant temps de l'insérer dans un tableau.

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

Tout ce que j'ai fait a été d'ajouter INTO JsonCats à ma requête, afin de créer une table appelée JsonCats et insérez-y les résultats de la requête.

Maintenant, sélectionnons le contenu de cette table.

SELECT * FROM JsonCats;

Résultat :

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

Le contenu est exactement tel que nous l'avons vu dans l'exemple précédent.

Et juste pour être absolument sûr, nous pouvons maintenant utiliser le sys.column vue du catalogue système vérifier les noms et les types de colonne de la table.

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

Résultat :

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

Encore une fois, exactement comme nous l'avions spécifié.

Notez que sys.columns renvoie toujours un max_length de -1 lorsque le type de données de la colonne est varchar(max) , nvarchar(max) , varbinaire(max) , ou xml . Nous avons spécifié nvarchar(max) et donc la valeur de -1 est exactement comme prévu.

Mode de chemin :laxiste ou strict

Le chemin fourni dans le deuxième argument ou dans le WITH la clause peut (optionnellement) commencer par le lax ou strict mot-clé.

  • En lax mode, OPENJSON() ne génère pas d'erreur si l'objet ou la valeur sur le chemin spécifié est introuvable. Si le chemin est introuvable, OPENJSON() renvoie soit un jeu de résultats vide, soit un NULL valeur.
  • En strict mode, OPENJSON() renvoie une erreur si le chemin est introuvable.

La valeur par défaut est lax , donc si vous ne spécifiez pas de mode de chemin, lax mode sera utilisé.

Voici quelques exemples pour illustrer ce qui se passe avec chaque mode lorsque le chemin est introuvable.

Deuxième argument

Dans les deux exemples suivants, je fournis un chemin inexistant dans le deuxième argument lors de l'appel de OPENJSON() . Le premier exemple montre ce qui se passe lors de l'utilisation du mode laxiste, le deuxième exemple montre ce qui se passe lors de l'utilisation du mode strict.

Mode laxiste

Voici ce qui se passe dans lax mode lorsque le chemin est introuvable.

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

Résultat :

(0 rows affected)

Pas d'erreur. Aucun résultat renvoyé.

Mode strict

Maintenant le voici en strict mode.

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

Résultat :

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

Comme prévu, le mode strict a généré une erreur.

Dans la clause AVEC

Dans les deux exemples suivants, nous testons à nouveau le mode laxiste par rapport au mode strict, sauf que cette fois nous le spécifions dans le WITH clause lors de la définition du schéma.

Mode laxiste

Voici ce qui se passe dans lax mode.

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

Résultat :

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

Dans ce cas, j'utilise 'lax $.born' parce que j'essaie de référencer une clé appelée born , mais une telle clé n'existe pas dans le JSON.

Cette fois, la colonne introuvable donne un NULL valeur.

Mode strict

Maintenant le voici en strict mode.

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

Résultat :

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

Cette fois j'ai utilisé 'strict $.born' .

Comme prévu, le mode strict a généré une erreur.

Niveau de compatibilité

Le OPENJSON() la fonction est disponible uniquement sous le niveau de compatibilité 130 ou supérieur.

Si le niveau de compatibilité de votre base de données est inférieur à 130, SQL Server ne pourra pas trouver et exécuter OPENJSON() , et vous obtiendrez une erreur.

Vous pouvez vérifier le niveau de compatibilité de votre base de données via le sys.databases vue du catalogue.

Vous pouvez modifier son niveau de compatibilité comme ceci :

ALTER DATABASE DatabaseName 
SET COMPATIBILITY_LEVEL = 150;

Nouveau sur JSON ?

Si vous n'êtes pas si familier avec JSON, consultez mon tutoriel JSON sur Quackit.