我有player
的表格 - 与skill
的多对多关系 - s
目标是列出球员及其前三名技能"只需一个查询。
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
);
查询:
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'
正如您在小提琴中看到的那样,查询的结果仅缺少3种技能的限制 我尝试了几种子查询的变化..连接等但没有效果。
答案 0 :(得分:75)
一种有点hacky的方法是对GROUP_CONCAT
的结果进行后期处理:
substring_index(group_concat(s.title SEPARATOR ','), ',', 3) as skills
当然,这假设您的技能名称不包含逗号,并且其数量相当小。
GROUP_CONCAT
LIMIT
支持明确player_skills
子句的feature request仍未解决。
更新:当用户Strawberry指出时,表(player_id, skill_id)
应该将元组group_concat
作为其主键,否则架构允许相同多次分配给玩家的技能,在这种情况下{{1}}将无法按预期工作。
答案 1 :(得分:18)
使用GROUP_CONCAT
增加GLOBAL group_concat_max_len
个函数长度
GROUP_CONCAT()
最大长度为1024个字符
您可以做的是在 mysql
GLOBAL group_concat_max_len
SET GLOBAL group_concat_max_len = 1000000;
试试这个,它肯定会起作用。
答案 2 :(得分:6)
有一个更清洁的解决方案。将其包含在另一个SELECT
语句中。
SELECT GROUP_CONCAT(id) FROM (
SELECT DISTINCT id FROM people LIMIT 4
) AS ids;
/* Result 134756,134754,134751,134750 */
答案 3 :(得分:5)
这是另一种解决方案。它包括一个解决关系的任意机制,并采用与你的略有不同的架构......
SELECT a.player_id
, GROUP_CONCAT(s.title ORDER BY rank) skills
FROM
( 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
) a
JOIN skill s
ON s.skill_id = a.skill_id
GROUP
BY player_id;
http://sqlfiddle.com/#!2/34497/18
顺便提一下,如果你有一个表示层/应用程序级代码,那么考虑在那里做所有的GROUP_CONCAT。它更灵活。
答案 4 :(得分:2)
如果您使用的是MariaDB 10.3.3+,则可能是这样:
对GROUP_CONCAT()中的LIMIT子句的支持(MDEV-11297)
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;
答案 5 :(得分:0)
您可以使用用户变量模拟分区的row_number,然后限制行并应用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;
此方法不需要任何表格多次读取。
答案 6 :(得分:0)
首先,设置组连接的限制,然后编写查询。
SET SESSION group_concat_max_len = 1200000;
select group_concat(column_name) from table_name group by column_name;
在这里,1200000表示查询最多允许1200000个字符用于组连接数据。