In SQLite, json_tree()
è una funzione con valori di tabella che esamina il valore JSON fornito come primo argomento e restituisce una tabella composta da una riga per ogni elemento dell'array o membro dell'oggetto.
Forniamo il valore JSON come argomento quando chiamiamo la funzione.
Possiamo opzionalmente passare un secondo argomento, che specifica un percorso da cui partire. Quando lo facciamo, json_tree()
considera quel percorso come l'elemento di primo livello.
Il json_tree()
La funzione percorre ricorsivamente la sottostruttura JSON a partire dall'elemento di livello superiore. Per visualizzare solo i figli immediati dell'array o dell'oggetto di livello superiore, o solo l'elemento di livello superiore stesso se l'elemento di livello superiore è un valore primitivo, usa json_each()
funzione invece.
Sintassi
Possiamo usare json_tree()
funzionare nei seguenti modi:
json_tree(X)
json_tree(X,P)
Dove X
rappresenta il JSON e P
è un argomento facoltativo che rappresenta il percorso da considerare come di livello superiore.
Esempio
Ecco un esempio per dimostrare come funziona:
SELECT * FROM json_tree('{ "name" : "Woof", "age" : 10 }');
Risultato:
+------+--------------------------+---------+------+----+--------+---------+------+ | 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 | $ | +------+--------------------------+---------+------+----+--------+---------+------+
Possiamo vedere che ogni membro dell'oggetto ha una propria riga con alcune informazioni utili, come il tipo (valore del testo SQL), il percorso, ecc.
Per quanto riguarda l'id
colonna, secondo la documentazione di SQLite si tratta di un numero di pulizia interno, il cui calcolo potrebbe cambiare nelle versioni future. L'unica garanzia è che l'id
la colonna sarà diversa per ogni riga.
Matrice
In questo esempio, il valore JSON è un array:
SELECT * FROM json_tree('[ 10, 30, 45 ]');
Risultato:
+------+------------+---------+------+----+--------+---------+------+ | 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] | $ | +------+------------+---------+------+----+--------+---------+------+
Specifica un percorso
Possiamo usare un secondo argomento per specificare un percorso da trattare come di livello superiore.
Esempio:
SELECT * FROM json_tree('{ "a" : 1, "b" : [ 4, 7, 8 ] }', '$.b');
Risultato:
+-----+---------+---------+------+----+--------+---------+------+ | 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 | +-----+---------+---------+------+----+--------+---------+------+
Documento più grande
In questo esempio useremo un documento JSON più grande. Per prima cosa, chiamiamo json_tree()
senza specificare un percorso:
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 ]
}
]'
);
Risultato:
+--------+--------------------------------------------------------------+---------+-------+------+--------+----------------+-------------+ | 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 | +--------+--------------------------------------------------------------+---------+-------+------+--------+----------------+-------------+
In questo caso, il nostro valore JSON è un array che contiene tre oggetti. Ogni oggetto è elencato nei risultati e ciascuno dei membri di tali oggetti è elencato, insieme ai rispettivi valori e così via.
Ora chiamiamo json_tree()
di nuovo, ma questa volta specificheremo un percorso:
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]'
);
Risultato:
+--------+----------------------------------------------+---------+------+----+--------+----------------+-------------+ | 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 | +--------+----------------------------------------------+---------+------+----+--------+----------------+-------------+
In questo caso ho scelto il secondo elemento dell'array specificando [1]
(gli array sono basati su zero in SQLite).
Il risultato è che l'albero è limitato solo al secondo elemento dell'array.
Andiamo più a fondo:
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'
);
Risultato:
+--------+------------+---------+------+----+--------+----------------+-------------+ | 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 | +--------+------------+---------+------+----+--------+----------------+-------------+
Più andiamo in profondità, meno righe vengono restituite. Questo perché stiamo ottenendo solo un albero che inizia nel percorso specifico che abbiamo specificato.
Filtraggio della query
Possiamo modificare la nostra query per filtrare i risultati in base a un determinato criterio. Ad esempio:
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%';
Risultato:
+-----------+--------------------------------------------------------------+ | 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 | +-----------+--------------------------------------------------------------+
Possiamo restituire solo l'ultima riga in questo modo:
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';
Risultato:
+------+-------+------+------+----+--------+-----------+------+ | key | value | type | atom | id | parent | fullkey | path | +------+-------+------+------+----+--------+-----------+------+ | user | Faye | text | Faye | 13 | 11 | $[1].user | $[1] | +------+-------+------+------+----+--------+-----------+------+
Restituisci i nodi foglia
Se vogliamo, possiamo limitare i risultati ai soli nodi foglia. Per "nodi foglia", intendo solo quelle chiavi che non hanno ulteriori elementi figlio.
Ci sono un paio di modi per farlo.
Un'opzione consiste nel filtrare tutti gli oggetti e gli array:
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');
Risultato:
+------+-------+---------+-------+----+--------+----------------+-------------+ | 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 | +------+-------+---------+-------+----+--------+----------------+-------------+
Un altro modo per farlo è controllare atom
colonna per valori nulli. In particolare, restituiremmo solo le righe in cui questa colonna doesn't
contengono un valore nullo:
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;
Risultato:
+------+-------+---------+-------+----+--------+----------------+-------------+ | 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
colonna è il valore SQL corrispondente agli elementi primitivi, elementi diversi dagli array e dagli oggetti JSON. Questa colonna è sempre null
per un array o un oggetto JSON. Il value
la colonna è la stessa di atom
colonna per elementi JSON primitivi ma assume il valore JSON di testo per matrici e oggetti.
Un esempio di database
Supponiamo di avere la seguente tabella:
SELECT * FROM scores;
Risultato:
+---------+---------------------------------------------------------+ | 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]}] | +---------+---------------------------------------------------------+
Questa tabella ha chiamato scores
ha due colonne. La prima colonna contiene il nome dell'insegnante e la seconda colonna contiene un documento JSON. Il documento JSON contiene gli studenti e i loro punteggi.
Ecco un esempio di esecuzione di una query che utilizza json_tree()
contro questa tabella:
SELECT
teacher,
key AS student,
value
FROM
scores,
json_tree(students, '$[1].Liz')
WHERE json_tree.key = 'Liz';
Risultato:
+---------+---------+---------+ | teacher | student | value | +---------+---------+---------+ | Stacy | Liz | [5,8,7] | +---------+---------+---------+
Qui abbiamo restituito tutti i punteggi di Liz e abbiamo anche restituito il suo insegnante.
La seguente query restituisce il suo secondo punteggio:
SELECT
teacher,
key AS student,
value AS score
FROM
scores,
json_tree(students, '$[1].Liz')
WHERE json_tree.key = 1;
Risultato:
+---------+---------+-------+ | teacher | student | score | +---------+---------+-------+ | Stacy | 1 | 8 | +---------+---------+-------+