簡體   English   中英

GROUP_CONCAT 有限制

[英]GROUP_CONCAT with limit

我與player -s 與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 個技能的限制。
我嘗試了幾種子查詢的變體……加入等等,但沒有效果。

一種有點hacky的方法是對GROUP_CONCAT的結果進行后處理:

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

當然,這假設您的技能名稱不包含逗號並且它們的數量相當小。

小提琴

很遺憾, GROUP_CONCAT支持顯式LIMIT子句的功能請求仍未解決。

更新:正如用戶Strawberry指出的那樣,表player_skills應該將元組(player_id, skill_id)作為其主鍵,否則模式允許將相同的技能多次分配給玩家,在這種情況下group_concat將無法正常工作預期的。

使用GLOBAL group_concat_max_len GROUP_CONCAT()增加GROUP_CONCAT函數長度,最大長度為 1024 個字符。
你可以做的是在mysql中設置GLOBAL group_concat_max_len

SET GLOBAL group_concat_max_len = 1000000;

試試這個,它肯定會起作用。

有一個更清潔的解決方案。 將它包裝在另一個SELECT語句中。

SELECT GROUP_CONCAT(id) FROM (
    SELECT DISTINCT id FROM people LIMIT 4
) AS ids;

/* Result 134756,134754,134751,134750 */

如果您使用的是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;

db<>小提琴演示

這是另一個解決方案。 它包括一種用於解決關系的任意機制,並采用與您的略有不同的模式......

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 的工作。 它更靈活。

您可以按照上述說明解決此類問題。

說明 1:設置組連接限制,然后編寫您的查詢。

SET SESSION group_concat_max_len = 1200000;

說明2:然后您可以按照給定的兩個示例找出您的解決方案。

示例 1:

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

示例 2:

select GROUP_CONCAT(caption)  from email_queue group BY process_type_id;

注1:這是example1example2查詢的表結構

. 在此處輸入圖片說明

注 2:這里 1200000 表示查詢允許最多 1200000 個字符的組連接數據。

您可以使用用戶變量模擬分區的 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;

演示

此方法不需要任何表多次讀取。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM