Dans SQLite, json_tree()
est une fonction table qui parcourt la valeur JSON fournie comme premier argument et renvoie une table composée d'une ligne pour chaque élément de tableau ou membre d'objet.
Nous fournissons la valeur JSON comme argument lorsque nous appelons la fonction.
Nous pouvons éventuellement passer un deuxième argument, qui spécifie un chemin à partir duquel commencer. Lorsque nous faisons cela, json_tree()
traite ce chemin comme l'élément de niveau supérieur.
Le json_tree()
La fonction parcourt de manière récursive la sous-structure JSON en commençant par l'élément de niveau supérieur. Pour parcourir uniquement les enfants immédiats du tableau ou de l'objet de niveau supérieur, ou uniquement l'élément de niveau supérieur lui-même si l'élément de niveau supérieur est une valeur primitive, utilisez le json_each()
fonction à la place.
Syntaxe
Nous pouvons utiliser le json_tree()
fonctionner de la manière suivante :
json_tree(X)
json_tree(X,P)
Où X
représente le JSON, et P
est un argument facultatif qui représente le chemin à traiter comme le niveau supérieur.
Exemple
Voici un exemple pour montrer comment cela fonctionne :
SELECT * FROM json_tree('{ "name" : "Woof", "age" : 10 }');
Résultat :
+------+--------------------------+---------+------+----+--------+---------+------+ | key | value | type | atom | id | parent | fullkey | path | +------+--------------------------+---------+------+----+--------+---------+------+ | null | {"name":"Woof","age":10} | object | null | 0 | null | $ | $ | | name | Woof | text | Woof | 2 | 0 | $.name | $ | | age | 10 | integer | 10 | 4 | 0 | $.age | $ | +------+--------------------------+---------+------+----+--------+---------+------+
Nous pouvons voir que chaque membre de l'objet a sa propre ligne avec des informations utiles, telles que son type (valeur de texte SQL), son chemin, etc.
Concernant l'id
colonne, selon la documentation SQLite, il s'agit d'un numéro de gestion interne, dont le calcul pourrait changer dans les versions futures. La seule garantie est que le id
la colonne sera différente pour chaque ligne.
Tableau
Dans cet exemple, la valeur JSON est un tableau :
SELECT * FROM json_tree('[ 10, 30, 45 ]');
Résultat :
+------+------------+---------+------+----+--------+---------+------+ | key | value | type | atom | id | parent | fullkey | path | +------+------------+---------+------+----+--------+---------+------+ | null | [10,30,45] | array | null | 0 | null | $ | $ | | 0 | 10 | integer | 10 | 1 | 0 | $[0] | $ | | 1 | 30 | integer | 30 | 2 | 0 | $[1] | $ | | 2 | 45 | integer | 45 | 3 | 0 | $[2] | $ | +------+------------+---------+------+----+--------+---------+------+
Spécifier un chemin
Nous pouvons utiliser un deuxième argument pour spécifier un chemin à traiter comme le niveau supérieur.
Exemple :
SELECT * FROM json_tree('{ "a" : 1, "b" : [ 4, 7, 8 ] }', '$.b');
Résultat :
+-----+---------+---------+------+----+--------+---------+------+ | key | value | type | atom | id | parent | fullkey | path | +-----+---------+---------+------+----+--------+---------+------+ | b | [4,7,8] | array | null | 4 | null | $.b | $ | | 0 | 4 | integer | 4 | 5 | 4 | $.b[0] | $.b | | 1 | 7 | integer | 7 | 6 | 4 | $.b[1] | $.b | | 2 | 8 | integer | 8 | 7 | 4 | $.b[2] | $.b | +-----+---------+---------+------+----+--------+---------+------+
Document plus grand
Dans cet exemple, nous utiliserons un document JSON plus volumineux. Appelons d'abord json_tree()
sans spécifier de chemin :
SELECT * FROM json_tree('[
{
"user" : "Spike",
"age" : 30,
"scores" : [ 9, 7, 3 ]
},
{
"user" : "Faye",
"age" : 25,
"scores" : [ 90, 87, 93 ]
},
{
"user" : "Jet",
"age" : 40,
"scores" : [ 50, 38, 67 ]
}
]'
);
Résultat :
+--------+--------------------------------------------------------------+---------+-------+------+--------+----------------+-------------+ | key | value | type | atom | id | parent | fullkey | path | +--------+--------------------------------------------------------------+---------+-------+------+--------+----------------+-------------+ | null | [{"user":"Spike","age":30,"scores":[9,7,3]},{"user":"Faye"," | array | null | 0 | null | $ | $ | | null | age":25,"scores":[90,87,93]},{"user":"Jet","age":40,"scores | null | null | null | null | null | null | | null | ":[50,38,67]}] | null | null | null | null | null | null | +--------+--------------------------------------------------------------+---------+-------+------+--------+----------------+-------------+ | 0 | {"user":"Spike","age":30,"scores":[9,7,3]} | object | null | 1 | 0 | $[0] | $ | +--------+--------------------------------------------------------------+---------+-------+------+--------+----------------+-------------+ | user | Spike | text | Spike | 3 | 1 | $[0].user | $[0] | +--------+--------------------------------------------------------------+---------+-------+------+--------+----------------+-------------+ | age | 30 | integer | 30 | 5 | 1 | $[0].age | $[0] | +--------+--------------------------------------------------------------+---------+-------+------+--------+----------------+-------------+ | scores | [9,7,3] | array | null | 7 | 1 | $[0].scores | $[0] | +--------+--------------------------------------------------------------+---------+-------+------+--------+----------------+-------------+ | 0 | 9 | integer | 9 | 8 | 7 | $[0].scores[0] | $[0].scores | +--------+--------------------------------------------------------------+---------+-------+------+--------+----------------+-------------+ | 1 | 7 | integer | 7 | 9 | 7 | $[0].scores[1] | $[0].scores | +--------+--------------------------------------------------------------+---------+-------+------+--------+----------------+-------------+ | 2 | 3 | integer | 3 | 10 | 7 | $[0].scores[2] | $[0].scores | +--------+--------------------------------------------------------------+---------+-------+------+--------+----------------+-------------+ | 1 | {"user":"Faye","age":25,"scores":[90,87,93]} | object | null | 11 | 0 | $[1] | $ | +--------+--------------------------------------------------------------+---------+-------+------+--------+----------------+-------------+ | user | Faye | text | Faye | 13 | 11 | $[1].user | $[1] | +--------+--------------------------------------------------------------+---------+-------+------+--------+----------------+-------------+ | age | 25 | integer | 25 | 15 | 11 | $[1].age | $[1] | +--------+--------------------------------------------------------------+---------+-------+------+--------+----------------+-------------+ | scores | [90,87,93] | array | null | 17 | 11 | $[1].scores | $[1] | +--------+--------------------------------------------------------------+---------+-------+------+--------+----------------+-------------+ | 0 | 90 | integer | 90 | 18 | 17 | $[1].scores[0] | $[1].scores | +--------+--------------------------------------------------------------+---------+-------+------+--------+----------------+-------------+ | 1 | 87 | integer | 87 | 19 | 17 | $[1].scores[1] | $[1].scores | +--------+--------------------------------------------------------------+---------+-------+------+--------+----------------+-------------+ | 2 | 93 | integer | 93 | 20 | 17 | $[1].scores[2] | $[1].scores | +--------+--------------------------------------------------------------+---------+-------+------+--------+----------------+-------------+ | 2 | {"user":"Jet","age":40,"scores":[50,38,67]} | object | null | 21 | 0 | $[2] | $ | +--------+--------------------------------------------------------------+---------+-------+------+--------+----------------+-------------+ | user | Jet | text | Jet | 23 | 21 | $[2].user | $[2] | +--------+--------------------------------------------------------------+---------+-------+------+--------+----------------+-------------+ | age | 40 | integer | 40 | 25 | 21 | $[2].age | $[2] | +--------+--------------------------------------------------------------+---------+-------+------+--------+----------------+-------------+ | scores | [50,38,67] | array | null | 27 | 21 | $[2].scores | $[2] | +--------+--------------------------------------------------------------+---------+-------+------+--------+----------------+-------------+ | 0 | 50 | integer | 50 | 28 | 27 | $[2].scores[0] | $[2].scores | +--------+--------------------------------------------------------------+---------+-------+------+--------+----------------+-------------+ | 1 | 38 | integer | 38 | 29 | 27 | $[2].scores[1] | $[2].scores | +--------+--------------------------------------------------------------+---------+-------+------+--------+----------------+-------------+ | 2 | 67 | integer | 67 | 30 | 27 | $[2].scores[2] | $[2].scores | +--------+--------------------------------------------------------------+---------+-------+------+--------+----------------+-------------+
Dans ce cas, notre valeur JSON est un tableau qui contient trois objets. Chaque objet est répertorié dans les résultats, et chacun des membres de ces objets est répertorié, ainsi que leurs valeurs respectives, et ainsi de suite.
Maintenant, appelons json_tree()
encore une fois, mais cette fois nous allons spécifier un chemin :
SELECT * FROM json_tree('[
{
"user" : "Spike",
"age" : 30,
"scores" : [ 9, 7, 3 ]
},
{
"user" : "Faye",
"age" : 25,
"scores" : [ 90, 87, 93 ]
},
{
"user" : "Jet",
"age" : 40,
"scores" : [ 50, 38, 67 ]
}
]',
'$[1]'
);
Résultat :
+--------+----------------------------------------------+---------+------+----+--------+----------------+-------------+ | key | value | type | atom | id | parent | fullkey | path | +--------+----------------------------------------------+---------+------+----+--------+----------------+-------------+ | null | {"user":"Faye","age":25,"scores":[90,87,93]} | object | null | 11 | null | $[0] | $ | | user | Faye | text | Faye | 13 | 11 | $[0].user | $[0] | | age | 25 | integer | 25 | 15 | 11 | $[0].age | $[0] | | scores | [90,87,93] | array | null | 17 | 11 | $[0].scores | $[0] | | 0 | 90 | integer | 90 | 18 | 17 | $[0].scores[0] | $[0].scores | | 1 | 87 | integer | 87 | 19 | 17 | $[0].scores[1] | $[0].scores | | 2 | 93 | integer | 93 | 20 | 17 | $[0].scores[2] | $[0].scores | +--------+----------------------------------------------+---------+------+----+--------+----------------+-------------+
Dans ce cas, j'ai choisi le deuxième élément du tableau en spécifiant [1]
(les tableaux sont basés sur zéro dans SQLite).
Le résultat est que l'arborescence est limitée au deuxième élément du tableau uniquement.
Allons plus loin :
SELECT * FROM json_tree('[
{
"user" : "Spike",
"age" : 30,
"scores" : [ 9, 7, 3 ]
},
{
"user" : "Faye",
"age" : 25,
"scores" : [ 90, 87, 93 ]
},
{
"user" : "Jet",
"age" : 40,
"scores" : [ 50, 38, 67 ]
}
]',
'$[1].scores'
);
Résultat :
+--------+------------+---------+------+----+--------+----------------+-------------+ | key | value | type | atom | id | parent | fullkey | path | +--------+------------+---------+------+----+--------+----------------+-------------+ | scores | [90,87,93] | array | null | 17 | null | $[0].scores | $[0] | | 0 | 90 | integer | 90 | 18 | 17 | $[0].scores[0] | $[0].scores | | 1 | 87 | integer | 87 | 19 | 17 | $[0].scores[1] | $[0].scores | | 2 | 93 | integer | 93 | 20 | 17 | $[0].scores[2] | $[0].scores | +--------+------------+---------+------+----+--------+----------------+-------------+
Plus nous allons en profondeur, moins de lignes sont renvoyées. C'est parce que nous n'obtenons qu'un arbre qui commence au chemin spécifique que nous avons spécifié.
Filtrer la requête
Nous pouvons modifier notre requête pour filtrer les résultats en fonction d'un critère donné. Par exemple :
SELECT
fullkey,
value
FROM json_tree('[
{
"user" : "Spike",
"age" : 30,
"scores" : [ 9, 7, 3 ]
},
{
"user" : "Faye",
"age" : 25,
"scores" : [ 90, 87, 93 ]
},
{
"user" : "Jet",
"age" : 40,
"scores" : [ 50, 38, 67 ]
}
]'
)
WHERE json_tree.value LIKE '%Faye%';
Résultat :
+-----------+--------------------------------------------------------------+ | fullkey | value | +-----------+--------------------------------------------------------------+ | $ | [{"user":"Spike","age":30,"scores":[9,7,3]},{"user":"Faye"," | | null | age":25,"scores":[90,87,93]},{"user":"Jet","age":40,"scores | | null | ":[50,38,67]}] | +-----------+--------------------------------------------------------------+ | $[1] | {"user":"Faye","age":25,"scores":[90,87,93]} | +-----------+--------------------------------------------------------------+ | $[1].user | Faye | +-----------+--------------------------------------------------------------+
Nous pouvons renvoyer uniquement la dernière ligne en procédant comme suit :
SELECT *
FROM json_tree('[
{
"user" : "Spike",
"age" : 30,
"scores" : [ 9, 7, 3 ]
},
{
"user" : "Faye",
"age" : 25,
"scores" : [ 90, 87, 93 ]
},
{
"user" : "Jet",
"age" : 40,
"scores" : [ 50, 38, 67 ]
}
]'
)
WHERE json_tree.value = 'Faye';
Résultat :
+------+-------+------+------+----+--------+-----------+------+ | key | value | type | atom | id | parent | fullkey | path | +------+-------+------+------+----+--------+-----------+------+ | user | Faye | text | Faye | 13 | 11 | $[1].user | $[1] | +------+-------+------+------+----+--------+-----------+------+
Renvoyer les nœuds feuilles
Nous pouvons limiter les résultats aux seuls nœuds feuilles si nous le voulons. Par "nœuds feuilles", j'entends uniquement les clés qui n'ont pas d'autres éléments enfants.
Il y a plusieurs façons de faire ça.
Une option consiste à filtrer tous les objets et tableaux :
SELECT *
FROM json_tree('[
{
"user" : "Spike",
"age" : 30,
"scores" : [ 9, 7, 3 ]
},
{
"user" : "Faye",
"age" : 25,
"scores" : [ 90, 87, 93 ]
},
{
"user" : "Jet",
"age" : 40,
"scores" : [ 50, 38, 67 ]
}
]'
)
WHERE json_tree.type NOT IN ('object','array');
Résultat :
+------+-------+---------+-------+----+--------+----------------+-------------+ | key | value | type | atom | id | parent | fullkey | path | +------+-------+---------+-------+----+--------+----------------+-------------+ | user | Spike | text | Spike | 3 | 1 | $[0].user | $[0] | | age | 30 | integer | 30 | 5 | 1 | $[0].age | $[0] | | 0 | 9 | integer | 9 | 8 | 7 | $[0].scores[0] | $[0].scores | | 1 | 7 | integer | 7 | 9 | 7 | $[0].scores[1] | $[0].scores | | 2 | 3 | integer | 3 | 10 | 7 | $[0].scores[2] | $[0].scores | | user | Faye | text | Faye | 13 | 11 | $[1].user | $[1] | | age | 25 | integer | 25 | 15 | 11 | $[1].age | $[1] | | 0 | 90 | integer | 90 | 18 | 17 | $[1].scores[0] | $[1].scores | | 1 | 87 | integer | 87 | 19 | 17 | $[1].scores[1] | $[1].scores | | 2 | 93 | integer | 93 | 20 | 17 | $[1].scores[2] | $[1].scores | | user | Jet | text | Jet | 23 | 21 | $[2].user | $[2] | | age | 40 | integer | 40 | 25 | 21 | $[2].age | $[2] | | 0 | 50 | integer | 50 | 28 | 27 | $[2].scores[0] | $[2].scores | | 1 | 38 | integer | 38 | 29 | 27 | $[2].scores[1] | $[2].scores | | 2 | 67 | integer | 67 | 30 | 27 | $[2].scores[2] | $[2].scores | +------+-------+---------+-------+----+--------+----------------+-------------+
Une autre façon de le faire est de vérifier le atom
colonne pour les valeurs nulles. Plus précisément, nous renvoyons uniquement les lignes où cette colonne doesn't
contenir une valeur nulle :
SELECT *
FROM json_tree('[
{
"user" : "Spike",
"age" : 30,
"scores" : [ 9, 7, 3 ]
},
{
"user" : "Faye",
"age" : 25,
"scores" : [ 90, 87, 93 ]
},
{
"user" : "Jet",
"age" : 40,
"scores" : [ 50, 38, 67 ]
}
]'
)
WHERE atom IS NOT NULL;
Résultat :
+------+-------+---------+-------+----+--------+----------------+-------------+ | key | value | type | atom | id | parent | fullkey | path | +------+-------+---------+-------+----+--------+----------------+-------------+ | user | Spike | text | Spike | 3 | 1 | $[0].user | $[0] | | age | 30 | integer | 30 | 5 | 1 | $[0].age | $[0] | | 0 | 9 | integer | 9 | 8 | 7 | $[0].scores[0] | $[0].scores | | 1 | 7 | integer | 7 | 9 | 7 | $[0].scores[1] | $[0].scores | | 2 | 3 | integer | 3 | 10 | 7 | $[0].scores[2] | $[0].scores | | user | Faye | text | Faye | 13 | 11 | $[1].user | $[1] | | age | 25 | integer | 25 | 15 | 11 | $[1].age | $[1] | | 0 | 90 | integer | 90 | 18 | 17 | $[1].scores[0] | $[1].scores | | 1 | 87 | integer | 87 | 19 | 17 | $[1].scores[1] | $[1].scores | | 2 | 93 | integer | 93 | 20 | 17 | $[1].scores[2] | $[1].scores | | user | Jet | text | Jet | 23 | 21 | $[2].user | $[2] | | age | 40 | integer | 40 | 25 | 21 | $[2].age | $[2] | | 0 | 50 | integer | 50 | 28 | 27 | $[2].scores[0] | $[2].scores | | 1 | 38 | integer | 38 | 29 | 27 | $[2].scores[1] | $[2].scores | | 2 | 67 | integer | 67 | 30 | 27 | $[2].scores[2] | $[2].scores | +------+-------+---------+-------+----+--------+----------------+-------------+
L'atom
colonne est la valeur SQL correspondant aux éléments primitifs - éléments autres que les tableaux et objets JSON. Cette colonne est toujours null
pour un tableau ou un objet JSON. La value
colonne est la même que l'atom
colonne pour les éléments JSON primitifs mais prend la valeur texte JSON pour les tableaux et les objets.
Un exemple de base de données
Supposons que nous ayons le tableau suivant :
SELECT * FROM scores;
Résultat :
+---------+---------------------------------------------------------+ | teacher | students | +---------+---------------------------------------------------------+ | Zohan | [{"Amy":[10,8,9]},{"Josh":[3,2,4]},{"Igor":[7,6,5]}] | | Stacy | [{"Pete":[5,3,1]},{"Liz":[5,8,7]},{"Jet":[9,5,7]}] | | Aisha | [{"Zolton":[4,6,7]},{"Bree":[7,7,4]},{"Rohit":[9,8,8]}] | +---------+---------------------------------------------------------+
Ce tableau appelé scores
a deux colonnes. La première colonne contient le nom de l'enseignant et la deuxième colonne contient un document JSON. Le document JSON contient les étudiants et leurs scores.
Voici un exemple d'exécution d'une requête qui utilise json_tree()
contre ce tableau :
SELECT
teacher,
key AS student,
value
FROM
scores,
json_tree(students, '$[1].Liz')
WHERE json_tree.key = 'Liz';
Résultat :
+---------+---------+---------+ | teacher | student | value | +---------+---------+---------+ | Stacy | Liz | [5,8,7] | +---------+---------+---------+
Ici, nous avons rendu toutes les partitions pour Liz, et nous avons également rendu son professeur.
La requête suivante renvoie son deuxième score :
SELECT
teacher,
key AS student,
value AS score
FROM
scores,
json_tree(students, '$[1].Liz')
WHERE json_tree.key = 1;
Résultat :
+---------+---------+-------+ | teacher | student | score | +---------+---------+-------+ | Stacy | 1 | 8 | +---------+---------+-------+