0

I want to get the Creators of Artworks, GROUP BY by their role. I find a way to have the list of the Creators with their role, for each Artwork.

SELECT 
    JSON_OBJECT('id', a.IDartwork, 'creaDate', a.artworkCreationDate, 'length', a.length, 'summarize', a.summarize) AS artwork,
    (SELECT JSON_ARRAYAGG(JSON_OBJECT('id', c.IDcreator, 'name', c.creatorName, 'role', r.roleName))
     FROM AMEGA_creator c 
     JOIN AMEGA_artwork_creator ac ON c.IDcreator = ac.IDcreator 
     JOIN AMEGA_role r ON r.IDrole = ac.IDrole
     WHERE ac.IDartwork = a.IDartwork) AS creators
FROM 
    AMEGA_artwork a;

But I can't get to use the GROUP BY. I get the error:

Subquery returns more than 1 row

I can't find a solution. I want a result like that :

artworks         creators
 <art1>          < <"director", <crea1, crea26>>, <"writer", <crea5, crea12>> >
 <art2>          < <"drawer", <crea23, crea8>> >

Here is the MLD structure :

enter image description here

Any idea ?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
nikubik
  • 21
  • 7
  • Not sure that a subquery is needed here. What RDBMS are you using (mysql, oracle, postgres, sql server, snowflake, teradata, etc)? I think Oracle, Mysql, and DB2 all have a `json_arrayagg()` function but possibly the help we can give will be dependent on which platfrom you use. – JNevill Jan 16 '23 at 16:09
  • I use **Mysql**. I also try a group by with the final structure i want :
    `SELECT JSON_ARRAYAGG(JSON_OBJECT('role', r.roleName, 'crea', JSON_ARRAYAGG(JSON_OBJECT('id', c.IDcreator, 'name', c.creatorName)))) FROM AMEGA_creator c JOIN AMEGA_artwork_creator ac ON c.IDcreator = ac.IDcreator JOIN AMEGA_role r ON r.IDrole = ac.IDrole WHERE ac.IDartwork = 2 GROUP BY ac.IDrole;`
    But i get a 'invalid use of group by' error.
    – nikubik Jan 16 '23 at 16:13
  • I think you need to join in `AMEGA_artwork()` in your subquery FROM clause and then only have the subquery, moving your `JSON_OBJECT()` into that SELECT statement and then add a `GROUP BY JSON_OBJECT(...)` at the end. Your subquery in your SELECT is returning multiple rows and isn't correlated to your `Amega_Artwork` table so you end up an error (more than one row returned from subquery) and even if that didn't error, you would have a cartesian product which is definitely not what you are after. – JNevill Jan 16 '23 at 16:31
  • Thank you for the answer, but i'm not sure to understand. AMEGA_artwork is already called, no problem joining a second time in the subquery ? I use a different alias for it ? I don't understand either how to make the subquery only after. I get the point of the multiple rows from the subquery, but don't figure out and see in my head how to handle. – nikubik Jan 16 '23 at 16:46

1 Answers1

1

Here's a bit of a guess on how this should look from my comment. Essentially somewhere in your SQL (either via correlated subquery's where clause or through a single SELECT's FROM clause) you have to establish a relationship between AMEGA_artwork and the rest of the tables. You went with a correlated subquery which is a good choice. The issue though is that for each AMEGA_artwork record there is more than one aggregated JSON_ARRAYAGG() result in that subquery. Normally this would be fine, but because your subquery is in your SELECT clause you can only have a single record come through for each AMEGA_artwork record. Otherwise you get that error.

I've switched this to a single FROM clause to establish the relationships between your table, and then a single JSON_OBJECT leading to your artwork output column and an aggregated JSON_ARRAYAGG() of the remaining column.

SELECT 
     JSON_OBJECT('id', a.IDartwork, 'creaDate', a.artworkCreationDate, 'length', a.length, 'summarize', a.summarize) AS artwork,
     JSON_ARRAYAGG(JSON_OBJECT('id', c.IDcreator, 'name', c.creatorName, 'role', r.roleName)
FROM
    AMEGA_artwork a
    JOIN AMEGA_artwork_creator ac 
       ON a.IDartwork = ac.IDartwork
    JOIN AMEGA_creator c     
       ON c.IDcreator = ac.IDcreator 
    JOIN AMEGA_role r
       ON r.IDrole = ac.IDrole        
GROUP BY artwork;

The different here is that we establish the relationship between AMEGA_artwork and AMEGA_artwork_creator in the FROM clause.

What you will likely see on running this is more than one record for each artwork. Which one is correct, or which one you want to keep, or how you want to further aggregate the data to reduce it to a single record, will be up to you.

JNevill
  • 46,980
  • 4
  • 38
  • 63
  • Working on revising my write up (sql isn't changing). I incorrectly thought you didn't have a correlated subquery, but in fact you do. That was a good step and I missed it. – JNevill Jan 16 '23 at 17:15
  • Edit has been made. Sorry again for missing that you had correlated your subquery. – JNevill Jan 16 '23 at 17:17
  • Thank you very much for your thorough answer. It does aggregate well the rows into one array. But, their always a but else it is not funny, i'm wondering if i can also group the Creators by role. I try this :
    `JSON_ARRAYAGG(JSON_OBJECT('role', r.roleName, 'crea', JSON_ARRAYAGG(JSON_OBJECT('id', c.IDcreator, 'name', c.creatorName)))) as creators,
    `. But i have an error on GROUP clause.
    – nikubik Jan 17 '23 at 08:40
  • And i want a structure like this : ------------------------------------------------------------------------------- `artworks creators < <"director", >, <"writer", > > < <"drawer", > >` I'm sorry, i can't display it well in the comments. It is visible in the top question. Do you think it is possible ? Maybe i need to subquery the creator part ? – nikubik Jan 17 '23 at 08:45