web-dev-qa-db-ja.com

MySQLグループは複数のテーブルの複数の列でグループごとに1つの結果のみ

3つの異なるテーブルがあり、特定の値(COUNT(track))を持つ行の数を数え、tracks.idでグループ化し、ユーザーごとに1つの結果(tracks.uid)だけで並べ替える必要があります。

私の3つのテーブル:

`users`
+--------+-------------------+
| `idu`  | `username`        | 
+--------+-------------------+
| 567    | 'TrainingPuppy'   | 
| 687    | 'BathroomMakeover'|
| 45     | 'PoshNachos'      | 
| 15     | 'SewingButtons'   | 
+--------+-------------------+

`views`
+--------+------+---------+
| `id`   | `by` | `track` |
+--------+------+---------+
| 1      | 5    | 55      | 
| 2      | 5    | 55      | 
| 3      | 67   | 55      | 
| 4      | 6    | 29      | 
| 5      | 125  | 2       | 
| 6      | 5    | 698     | 
| 7      | 5    | 698     | 
+--------+------+---------+

`tracks`
+--------+-------+-----------------------+---------------------+
| `id`   | `uid` | `title`               | `time`              |
+--------+-------+-----------------------+---------------------+
| 2      | 15    | 'Worf is in the air'  | 2016-02-11 22:57:35 |
| 29     | 567   | 'Stargold'            | 2016-08-11 22:57:28 |
| 55     | 567   | 'No love liers'       | 2016-10-11 22:57:51 | 
| 698    | 567   | 'Lofe'                | 2016-11-11 22:57:44 |
+--------+-------+-----------------------+---------------------+

だから私は試しました:

SELECT `views`.`track`, `tracks`.*, `users`.*, COUNT(`track`) as `count` 
FROM `views`,`tracks`,`users` 
WHERE `views`.`track` = `tracks`.`id` 
    AND `tracks`.`uid` = `users`.`idu`  
GROUP BY `tracks`.`uid` 
ORDER BY `count` 
DESC LIMIT 0, 20

[〜#〜]結果[〜#〜]

+--------+-------+---------+---------------------+----------+-----------------------+---------------------+
| `id`   | `uid` | `count` | `username`          | `track`  | `title`               | `time`              |
+--------+-------+---------+---------------------+----------+-----------------------+---------------------+
| 29     | 567   |  6      | 'TrainingPuppy'     | 29       | 'Stargold'            | 2016-10-11 22:57:51 | 
| 2      | 15    |  1      | 'SewingButtons'     | 2        | 'Worf is in the air'  | 2016-02-11 22:57:35 |
+--------+-------+---------+---------------------+----------+-----------------------+---------------------+

代わりに、各ユーザーの1つのトラック(ビューテーブルで最もカウントされる)のみを選択する必要があります。以下のようなもの:

+--------+-------+---------+---------------------+----------+---------------------+---------------------+
| `id`   | `uid` | `count` | `username`          | `track`  | `title`             | `time`              |
+--------+-------+---------+---------------------+----------+---------------------+---------------------+
| 55     | 567   |  3      | 'TrainingPuppy'     | 55       | 'No love liers'     | 2016-10-11 22:57:51 | 
| 2      | 15    |  1      | 'SewingButtons'     | 2        | 'Worf is in the air'| 2016-02-11 22:57:35 |
+--------+-------+---------+---------------------+----------+---------------------+---------------------+

どうすればこれを達成できますか?

1
NineCattoRules

テーブルといくつかのサンプルデータを設定します。

drop table if exists users;
drop table if exists views;
drop table if exists tracks;

create table users
(idu      int
,username varchar(30));

create table views
(id       int
,`by`     int
,track    int);

create table tracks
(id       int
,uid      int
,title    varchar(30)
,time     datetime);

insert into users values
(567,'TrainingPuppy'),
(687,'BathroomMakeover'),
( 45,'PoshNachos'),
( 15,'SewingButtons');

insert into views values
(1,  5, 55),
(2,  5, 55),
(3, 67, 55),
(4,  6, 29),
(5,125,  2),
(6,  5,698),
(7,  5,698);

insert into tracks values
(  2, 15,'Worf is in the air','2016-02-11 22:57:35'),
( 29,567,'Stargold'          ,'2016-08-11 22:57:28'),
( 55,567,'No love liers'     ,'2016-10-11 22:57:51'),
(698,567,'Lofe'              ,'2016-11-11 22:57:44');

(ANSI標準group by)クエリから始めて、カウントを取得します。

select tracks.id,
       tracks.uid,
       count(*) as ucount,
       users.username,
       views.track,
       tracks.title,
       tracks.time

from   tracks
join   views
on     views.track = tracks.id

join   users
on     users.idu = tracks.uid

group by tracks.id,
         tracks.uid,
         users.username,
         views.track,
         tracks.title,
         tracks.time

上記の「user_counts」を呼び出して、ucount descで順序付けされたusernameのランキングを生成するクエリを作成します。

select id,
       uid,
       ucount,
       username,
       track,
       title,
       time,
       @user_rank := if(@current_username = username, @user_rank:=@user_rank+1, 1) as user_rank,
       @current_username := username

from   user_counts,

        -- initialize our variables
join   (select @user_rank := 0, @current_username := 'abcdefg') r

order by username, ucount desc

次に、これを最後のselectでラップし、ランキングが「1」の行のみが表示されるようにします。すべてを1つのクエリにまとめて、次のようにします。

select id,
       uid,
       ucount as 'count',
       username,
       track,
       title,
       time

from   (select id,
               uid,
               ucount,
               username,
               track,
               title,
               time,
               @user_rank        := if(@current_username = username, @user_rank:=@user_rank+1, 1) as user_rank,
               @current_username := username

        from   (select tracks.id,
                       tracks.uid,
                       count(*) as ucount,
                       users.username,
                       views.track,
                       tracks.title,
                       tracks.time

                from    tracks
                join    views
                on      views.track = tracks.id

                join    users
                on      users.idu = tracks.uid

                group by tracks.id,
                         tracks.uid,
                         users.username,
                         views.track,
                         tracks.title,
                         tracks.time) user_counts

                -- initialize our variables
        join   (select @user_rank := 0, @current_username := 'abcdefg') r

        order by username, ucount desc) dt

where   user_rank = 1

order by ucount desc, username

そして結果:

id uid count username      track title              time
-- --- ----- ------------- ----- ------------------ -------------------
55 567     3 TrainingPuppy    55 No love liers      2016-10-11 22:57:51
 2  15     1 SewingButtons     2 Worf is in the air 2016-02-11 22:57:35

いくつかのフィドル:


注:指定されたusernameが複数のトラック/タイトルに対して同じmax(count)を持つ場合、このソリューションは1つのレコードのみを表示します。複数のレコードを表示するには、それらのレコードが確実に同じランキング(つまり、user_rank = 1)を受け取るように調整する必要があります。

3
markp-fuso