sqlsqlitesqlite-json1

Aggregate SQLite query across multiple tables using JSON1


I can't get my head around the following problem. The other day I learned how to use the JSON1 family of functions, but this time it seems to be more of an SQL issue.

This is my database setup:

CREATE TABLE persons(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE)
CREATE TABLE interests(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE)
CREATE TABLE persons_interests(person INTEGER, interest INTEGER, FOREIGN KEY(person) REFERENCES persons(id), FOREIGN KEY(interest) REFERENCES interests(id))

INSERT INTO persons(name) VALUES('John')
INSERT INTO persons(name) VALUES('Jane')

INSERT INTO interests(name) VALUES('Cooking')
INSERT INTO interests(name) VALUES('Gardening')
INSERT INTO interests(name) VALUES('Relaxing')

INSERT INTO persons_interests VALUES(1, 1)
INSERT INTO persons_interests VALUES(1, 2)
INSERT INTO persons_interests VALUES(2, 3)

Based on this data I'd like to get the following output, which is all interests of all persons aggregated into a single JSON array:

[{name: John, interests:[{name: Cooking},{name: Gardening}]}, {name: Jane, interests:[{name: Relaxing}]}]

Now the following is what I tried to do. Needless to say, this doesn't give me what I want:

SELECT p.name, json_object('interests', json_group_array(json_object('name', i.name))) interests
FROM persons p, interests i
JOIN persons_interests pi ON pi.person = p.id AND pi.interest = i.id

The undesired output is:

John|{"interests":[{"name":"Cooking"},{"name":"Gardening"},{"name":"Relaxing"}]}

Any help is highly appreciated!


Solution

  • For using json_group_array you must group line , in your case by person , except you want only one row with all your results .

    Example 1)

    This first version , will give you 1 json object by person , so the result will be N rows for N persons :

    SELECT json_object( 'name ',
                        p.name, 
                        'interests', 
                        json_group_array(json_object('name', i.name))) jsobjects
    FROM persons p, interests i
    JOIN persons_interests pi ON pi.person = p.id AND pi.interest = i.id
    group by p.id ;
    

    Example 2)

    This second version , will give return 1 big json array that contains all persons , but you fetch only one row .

    SELECT json_group_array(jsobjects) 
    FROM (
    
        SELECT json_object( 'name ',
                            p.name, 
                            'interests', 
                            json_group_array(json_object('name', i.name))) jsobjects
        FROM persons p, interests i
        JOIN persons_interests pi ON pi.person = p.id AND pi.interest = i.id
        group by p.id 
    ) jo ;