相关文章推荐
乐观的皮带  ·  excel ...·  1 年前    · 
风度翩翩的创口贴  ·  关于HIVE中UNION ...·  1 年前    · 
豪气的地瓜  ·  鸿蒙中 ...·  1 年前    · 
Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I have table with player -s in many-to-many relation with skill -s

The goal is to list the players and their "top 3 skills" with a single query.

fiddle

create table player(
  id int primary key
create table skill(
  id int primary key,
  title varchar(100)
create table player_skills (
  id int primary key,
  player_id int,
  skill_id int,
  value int

Query:

SELECT 
p.id,  
group_concat(s.title  SEPARATOR ', ') as skills
FROM player p
LEFT JOIN player_skills ps ON ps.player_id = p.id
LEFT JOIN skill s ON s.id = ps.skill_id
WHERE ps.value > 2
-- skills limit 3 some how ...
group by p.id 
order by s.id
-- expected result
-- player_ID, skills
-- 1 , 'one'
-- 2 , 'one'
-- 3 , 'two, three, four'

As you can see in the fiddle the result of the query is missing only the limit of 3 skills.
I tried several variation of sub queries.. joins and so but with no effect.

One somewhat hacky way to do it is to post-process the result of GROUP_CONCAT:

substring_index(group_concat(s.title SEPARATOR ','), ',', 3) as skills

Of course this assumes that your skill names don't contain commas and that their amount is reasonably small.

fiddle

A feature request for GROUP_CONCAT to support an explicit LIMIT clause is unfortunately still not resolved.

UPDATE: As user Strawberry points out, the table player_skills should have the tuple (player_id, skill_id) as its primary key, otherwise the schema allows for the same skill to be assigned to a player multiple times, in which case group_concat would not work as expected.

brilliant :) this is way better then several nested queries as the skills will be limited any way. – d.raev May 12, 2014 at 12:09 Because of the strange (and probably redundant) PK, this should be GROUP_CONCAT(DISTINCT... – Strawberry May 12, 2014 at 12:36 @Strawberry Thanks, I added a comment about this in my answer. Adding proper constraints is probably the way to go – Niklas B. May 12, 2014 at 12:49 I expected more proper way but this hack is way cleaner and simple which makes it better for my case then several nested queries. Thanks for the out of the box thinking. – d.raev May 13, 2014 at 6:26 Very nice. I would be concerned about the order of the results and would add an order by clause within the GROUP_CONCAT. – Jim Mc May 17, 2016 at 17:04

Increase GROUP_CONCAT function length using GLOBAL group_concat_max_len GROUP_CONCAT() maximum length is 1024 characters.
What you can do is to set the GLOBAL group_concat_max_len in mysql

SET GLOBAL group_concat_max_len = 1000000;

Try this and it will work for sure.

There is a much cleaner solution. Wrap it inside another SELECT statement.

SELECT GROUP_CONCAT(id) FROM (
    SELECT DISTINCT id FROM people LIMIT 4
) AS ids;
/* Result 134756,134754,134751,134750 */
                MySQL can't handle LIMITs inside of subqueries. It's been driving me mad for quite a while now :-/
– Theodore R. Smith
                May 22, 2018 at 19:18
SELECT p.id,  
   GROUP_CONCAT(s.title ORDER BY title  SEPARATOR ', ' LIMIT 3) as skills
FROM player p
LEFT JOIN player_skills ps ON ps.player_id = p.id
LEFT JOIN skill s ON s.id = ps.skill_id
WHERE ps.value > 2
GROUP BY p.id 
ORDER BY s.id;

db<>fiddle demo

SELECT GROUP_CONCAT(questionbankID ORDER BY RAND() SEPARATOR ', ' LIMIT 3 ) as questionbankID FROM questionbank WHERE clientID = 82 AND lessonID = 184 AND questionType = 'objective' AND type = 'A' Very similar Query is Giving me Error .. – Alok Jha May 24, 2021 at 12:07

Here's another solution. It includes an arbitrary mechanism for resolving ties, and employes a schema slightly differing from yours...

SELECT a.player_id
     , GROUP_CONCAT(s.title ORDER BY rank) skills
     ( SELECT x.*, COUNT(*) rank
         FROM player_skills x
         JOIN player_skills y 
           ON y.player_id = x.player_id
          AND (y.value > x.value
           OR (y.value = x.value AND y.skill_id <= x.skill_id))
        GROUP 
           BY player_id, value, skill_id
       HAVING COUNT(*) <= 3
  JOIN skill s
    ON s.skill_id = a.skill_id
 GROUP 
    BY player_id;

http://sqlfiddle.com/#!2/34497/18

Incidentally, if you have a presentation layer/application-level code, then consider doing all the GROUP_CONCAT stuff there. It's more flexible.

You can follow the mentioned instructions to solve this kind of problems.

Instrcution1: Set the limit for group concate then write your query.

SET SESSION group_concat_max_len = 1200000;

Instruction 2: Then you can follow the given two example to find out your solution.

Example 1:

SELECT GROUP_CONCAT(app_id) AS ids FROM (
      SELECT DISTINCT app_id FROM email_queue 
) AS ids;

Example 2:

select GROUP_CONCAT(caption)  from email_queue group BY process_type_id;

Note 1: This is the table structure for the query of example1 and example2

Note 2: Here, 1200000 means that the query allow 1200000 characters maximum for group concate data.

Can you explain why one should set the limit? What's that needed for? The accepted answer does not contain this – Nico Haase Apr 24, 2019 at 9:08 when the related column of GROUP_CONCAT contain more then 1024 characters then you need to use this for getting full result of GROUP_CONCAT function. For more details: namasteui.com/mysql-group_concat-maximum-length – Majbah Habib Jan 7, 2020 at 11:56

You can simulate the partitioned row_number using user variables and then limit rows and apply group_concat:

select p.id,
    group_concat(s.title separator ', ') as skills
from player p
left join (
    select distinct ps.player_id,
        ps.skill_id,
        @rn := if(@player_id = player_id, @rn+1, if(@player_id := player_id, 1, 1)) as seqnum
    from player_skills ps
    cross join (select @rn := 0, @player_id := null) x
    where ps.value > 2
    order by player_id, value desc
    ) ps on p.id = ps.player_id and ps.seqnum <= 3
left join skill s on ps.skill_id = s.id
group by p.id;

This method doesn't require any table to read more than once.

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.