web-dev-qa-db-ja.com

GROUP_CONCAT制限付き

player- sと多対多の関係にあるskill- sを持つテーブルがあります

目標は、プレーヤーとその「トップ3スキル」を1つのクエリでリストすることです。

フィドル

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スキルの制限のみが欠落しています。
サブクエリのいくつかのバリエーションを試してみました。

41
d.raev

ややハックする方法の1つは、GROUP_CONCATの結果を後処理することです。

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

もちろん、これはスキル名にカンマが含まれておらず、その量が適度に少ないことを前提としています。

フィドル

GROUP_CONCATが明示的なLIMIT句をサポートするための 機能リクエスト は、残念ながらまだ解決されていません。

[〜#〜] update [〜#〜]:ユーザー Strawberry が指摘するように、表player_skillsには主キーとしてタプル(player_id, skill_id)が必要です。そうでない場合、スキーマは同じスキルをプレイヤーに複数回割り当てることを許可します。その場合、group_concatは期待どおりに機能しません。

77
Niklas B.

_GROUP_CONCAT_ GROUP_CONCAT()を使用して_GLOBAL group_concat_max_len_関数の長さを増やしてください。最大長は1024文字です。
できることは、mysqlで_GLOBAL group_concat_max_len_を設定することです

_SET GLOBAL group_concat_max_len = 1000000;
_

これを試してみると、確実に機能します。

18
Zaib Khan

よりクリーンなソリューションがあります。別のSELECTステートメントで囲みます。

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

/* Result 134756,134754,134751,134750 */
7
Romain Bruckert

別のソリューションがあります。それは関係を解決するための任意のメカニズムを含み、あなたのものとわずかに異なるスキーマを採用しています...

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を行うことを検討してください。より柔軟です。

5
Strawberry

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 <> fiddle demo

2
Lukasz Szozda

まず、グループ連結の制限を設定してから、クエリを記述します。

SET SESSION group_concat_max_len = 1200000;

select group_concat(column_name) from table_name group by column_name;

ここで、1200000は、クエリがグループ連結データに最大1200000文字を許可することを意味します。

0
Majbah Habib

ユーザー変数を使用してパーティション化された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;

デモ

このメソッドでは、テーブルを複数回読み取る必要はありません。

0
Gurwinder Singh