Использование GROUP_CONCAT для подзапроса в MySQL - PullRequest
38 голосов
/ 18 декабря 2008

У меня есть запрос MySQL, в который я хочу включить список идентификаторов из другой таблицы. На веб-сайте люди могут добавлять определенные элементы, а затем они могут добавлять эти элементы в избранное. Я в основном хочу получить список идентификаторов людей, которые одобрили этот элемент (это немного упрощено, но это то, к чему это сводится).

В основном я делаю что-то вроде этого:

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

Таким образом, я смогу показать, кто предпочитает какой-то элемент, позже разделив idlist на массив в PHP и далее в моем коде, однако я получаю следующую ошибку MySQL:

1242 - подзапрос возвращает более 1 строки

Я подумал, что было бы смысл использовать GROUP_CONCAT вместо, например, CONCAT? Я поступаю неправильно?


Хорошо, спасибо за ответы, похоже, это работает. Однако здесь есть одна загвоздка. Элементы также считаются избранными, если они были добавлены этим пользователем. Поэтому мне понадобится дополнительная проверка, чтобы убедиться, что creator = userid. Может кто-нибудь помочь мне придумать умный (и, надеюсь, эффективный) способ сделать это?

Спасибо!

Редактировать: я просто пытался сделать это:

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

И idlist пусто. Обратите внимание, что если я использую INNER JOIN вместо LEFT JOIN, я получу пустой результат. Хотя я уверен, что есть строки, которые соответствуют требованию ON.

Ответы [ 5 ]

88 голосов
/ 25 марта 2012

ОП почти правильно понял. GROUP_CONCAT должен заключать столбцы в подзапрос, а не в полный подзапрос (я исключаю разделитель, потому что запятая используется по умолчанию):

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

Это даст желаемый результат, а также означает, что принятый ответ частично неверен, потому что вы можете получить доступ к переменным внешней области в подзапросе.

13 голосов
/ 18 декабря 2008

Вы не можете получить доступ к переменным во внешней области в таких запросах (там нельзя использовать items.id). Вы должны попробовать что-то вроде

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;

Раскройте список полей по мере необходимости (имя, цвет ...).

2 голосов
/ 19 декабря 2008

Я думаю, что вы можете неправильно использовать "userid = itemid", не должно ли это быть так:

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
1 голос
/ 15 декабря 2011

Да, решение Soulmerge в порядке. Но мне нужен был запрос, где мне нужно было собрать данные из нескольких дочерних таблиц, например:

  • основная таблица: сеансы (сеансы презентации) (uid, name, ..)
  • 1-я дочерняя таблица: события с ключом session_id (uid, session_uid, date, time_start, time_end)
  • 2-й дочерний стол: вспомогательное оборудование_ необходимое (ноутбук, проектор, микрофон и т. Д.) С ключом session_id (uid, session_uid, accessory_name)
  • 3-я дочерняя таблица: session_presenters (лица, ведущие презентации) с ключом session_id (uid, session_uid, имя_ презентатора, адрес ...)

У каждого сеанса больше строк в таблицах дочерних таблиц (больше расписаний, больше аксессуаров)

И мне нужно было собрать по одной коллекции для каждой сессии, чтобы отобразить ее в ряду (некоторые из них):

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

Мое решение (после многих часов экспериментов):

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

Так что не требуется JOIN или GROUP BY. Еще одна полезная вещь для отображения данных, дружественных (при «повторении» их):

  • Вы можете обернуть events.date, time_start, time_end и т. Д. В "
    • ...
    ", так что "

Надеюсь, это кому-нибудь поможет. Ура!

1 голос
/ 18 декабря 2008

Назначение GROUP_CONCAT верно, но подзапрос не нужен и вызывает проблему. Попробуйте вместо этого:

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
...