Using GROUP_CONCAT on subquery in MySQL

MysqlSqlGroup ConcatMysql Error-1242

Mysql Problem Overview


I have a MySQL query in which I want to include a list of ID's from another table. On the website, people are able to add certain items, and people can then add those items to their favourites. I basically want to get the list of ID's of people who have favourited that item (this is a bit simplified, but this is what it boils down to).

Basically, I do something like this:

SELECT *,
GROUP_CONCAT((SELECT userid FROM favourites WHERE itemid = items.id) SEPARATOR ',') AS idlist
FROM items
WHERE id = $someid

This way, I would be able to show who favourited some item, by splitting the idlist later on to an array in PHP further on in my code, however I am getting the following MySQL error:

>1242 - Subquery returns more than 1 row

I thought that was kind of the point of using GROUP_CONCAT instead of, for example, CONCAT? Am I going about this the wrong way?


Ok, thanks for the answers so far, that seems to work. However, there is a catch. Items are also considered to be a favourite if it was added by that user. So I would need an additional check to check if creator = userid. Can someone help me come up with a smart (and hopefully efficient) way to do this?

Thank you!

Edit: I just tried to do this:

SELECT [...] LEFT JOIN favourites ON (userid = itemid OR creator = userid)

And idlist is empty. Note that if I use INNER JOIN instead of LEFT JOIN I get an empty result. Even though I am sure there are rows that meet the ON requirement.

Mysql Solutions


Solution 1 - Mysql

OP almost got it right. GROUP_CONCAT should be wrapping the columns in the subquery and not the complete subquery (I'm dismissing the separator because comma is the default):

SELECT i.*,
(SELECT GROUP_CONCAT(userid) FROM favourites f WHERE f.itemid = i.id) AS idlist
FROM items i
WHERE i.id = $someid

This will yield the desired result and also means that the accepted answer is partially wrong, because you can access outer scope variables in a subquery.

Solution 2 - Mysql

You can't access variables in the outer scope in such queries (can't use items.id there). You should rather try something like

SELECT
    items.name,
    items.color,
    CONCAT(favourites.userid) as idlist
FROM
    items
INNER JOIN favourites ON items.id = favourites.itemid
WHERE
    items.id = $someid
GROUP BY
    items.name,
    items.color;

Expand the list of fields as needed (name, color...).

Solution 3 - Mysql

I think you may have the "userid = itemid" wrong, shouldn't it be like this:

SELECT ITEMS.id,GROUP_CONCAT(FAVOURITES.UserId) AS IdList
FROM FAVOURITES 
INNER JOIN ITEMS ON (ITEMS.Id = FAVOURITES.ItemId OR FAVOURITES.UserId = ITEMS.Creator)
WHERE ITEMS.Id = $someid
GROUP BY ITEMS.ID

Solution 4 - Mysql

The purpose of GROUP_CONCAT is correct but the subquery is unnecessary and causing the problem. Try this instead:

SELECT ITEMS.id,GROUP_CONCAT(FAVOURITES.UserId)
FROM FAVOURITES INNER JOIN ITEMS ON ITEMS.Id = FAVOURITES.ItemId
WHERE ITEMS.Id = $someid
GROUP BY ITEMS.ID

Solution 5 - Mysql

Yes, soulmerge's solution is ok. But I needed a query where I had to collect data from more child tables, for example:

  • main table: sessions (presentation sessions) (uid, name, ..)
  • 1st child table: events with key session_id (uid, session_uid, date, time_start, time_end)
  • 2nd child table: accessories_needed (laptop, projector, microphones, etc.) with key session_id (uid, session_uid, accessory_name)
  • 3rd child table: session_presenters (presenter persons) with key session_id (uid, session_uid, presenter_name, address...)

Every Session has more rows in child tables tables (more time schedules, more accessories)

And I needed to collect in one collection for every session to display in ore row (some of them):

session_id | session_name | date | time_start | time_end | accessories | presenters

My solution (after many hours of experiments):

SELECT sessions.uid, sessions.name,
    ,(SELECT GROUP_CONCAT( `events`.date SEPARATOR '</li><li>') 
            FROM `events` 
            WHERE `events`.session_id = sessions.uid ORDER BY `events`.date) AS date
    ,(SELECT GROUP_CONCAT( `events`.time_start SEPARATOR '</li><li>') 
            FROM `events` 
            WHERE `events`.session_id = sessions.uid ORDER BY `events`.date) AS time_start
    ,(SELECT GROUP_CONCAT( `events`.time_end SEPARATOR '</li><li>') 
            FROM `events` 
            WHERE `events`.session_id = sessions.uid ORDER BY `events`.date) AS time_end
    ,(SELECT GROUP_CONCAT( accessories.name SEPARATOR '</li><li>') 
            FROM accessories 
            WHERE accessories.session_id = sessions.uid ORDER BY accessories.name) AS accessories
    ,(SELECT GROUP_CONCAT( presenters.name SEPARATOR '</li><li>') 
            FROM presenters
            WHERE presenters.session_id = sessions.uid ORDER BY presenters.name) AS presenters

    FROM sessions

So no JOIN or GROUP BY needed. Another useful thing to display data friendly (when "echoing" them):

  • you can wrap the events.date, time_start, time_end, etc in "<UL><LI> ... </LI></UL>" so the "<LI></LI>" used as separator in the query will separate the results in list items.

I hope this helps someone. Cheers!

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAistinaView Question on Stackoverflow
Solution 1 - MysqlnietonfirView Answer on Stackoverflow
Solution 2 - MysqlsoulmergeView Answer on Stackoverflow
Solution 3 - MysqlTurnkeyView Answer on Stackoverflow
Solution 4 - MysqlTurnkeyView Answer on Stackoverflow
Solution 5 - MysqlAmbrus LaszloView Answer on Stackoverflow