【问题标题】:MySQL COUNT occurrences from several columns来自多列的 MySQL COUNT 次出现
【发布时间】:2018-11-24 18:42:45
【问题描述】:

我有一个名为games 的 4 人游戏的下表。

+---------+---------+---------+---------+---------+
| game_id | player1 | player2 | player3 | player4 |
+---------+---------+---------+---------+---------+
|    1001 | john    | dave    | NULL    | NULL    |
|    1002 | dave    | john    | mike    | tim     |
|    1003 | mike    | john    | dave    | NULL    |
|    1004 | tim     | dave    | NULL    | NULL    |
+---------+---------+---------+---------+---------+

我希望能够回答两个问题:

  1. 谁参加的比赛最多? (戴夫)
  2. 哪对玩家一起玩的游戏最多? (约翰和戴夫)

对于#1,我尝试调整我在这里找到的答案:mySQL query to find the most repeated value 但它似乎只能回答单个专栏的问题。这意味着我可以知道谁是player1 最多的人,但不能知道谁像任何玩家一样参加了最多的比赛:

SELECT player1 p1, COUNT(*) p1 FROM games
GROUP BY p1    
ORDER BY p1 DESC;

有没有办法将这些列连接在一起,或者我必须在应用程序代码中处理这个问题?

不知道从哪里开始 #2。我想知道我的表格结构是否应该将玩家合并到一个列中:

+----+---------+--------+
| id | game_id | player |
+----+---------+--------+
|  1 |    1001 | john   |
|  2 |    1001 | dave   |
|  3 |    1002 | john   |
|  4 |    1002 | dave   |
|  5 |    1002 | mike   |
|  6 |    1002 | tim    |
+----+---------+--------+

【问题讨论】:

    标签: mysql


    【解决方案1】:

    您最好的选择是规范化数据库。这是一个多对多的关系,需要一个链接表将一个游戏连接到它对应的玩家。那么计算会容易得多。不过,您可以为问题一使用派生表,将所有列合并为一个:

    SELECT `player`,
           COUNT(*) as `count`
    FROM
        (
            SELECT `player1` `player`
            FROM `games`
            UNION ALL
            SELECT `player2` `player`
            FROM `games`
            UNION ALL
            SELECT `player3` `player`
            FROM `games`
            UNION ALL
            SELECT `player4` `player`
            FROM `games`
        ) p
    GROUP BY `player` HAVING `player` IS NOT NULL
    ORDER BY `count` DESC
    

    live demo here

    对于第二个问题,您必须对派生表进行内部联接:

    SELECT `p`.`player`,
           `p2`.`player`,
           count(*) AS count
    FROM
        (
            SELECT `game_id`, `player1` `player`
            FROM `games`
            UNION ALL
            SELECT `game_id`, `player2` `player`
            FROM `games`
            UNION ALL
            SELECT `game_id`, `player3` `player`
            FROM `games`
            UNION ALL
            SELECT `game_id`, `player4` `player`
            FROM `games`
        ) p
    INNER JOIN
        (
            SELECT `game_id`, `player1` `player`
            FROM `games`
            UNION ALL
            SELECT `game_id`, `player2` `player`
            FROM `games`
            UNION ALL
            SELECT `game_id`, `player3` `player`
            FROM `games`
            UNION ALL
            SELECT `game_id`, `player4` `player`
            FROM `games`
        ) p2
    ON `p`.`game_id` = `p2`.`game_id` AND `p`.`player` < `p2`.`player`
    WHERE `p`.`player` IS NOT NULL AND `p2`.`player` IS NOT NULL
    GROUP BY `p`.`player`, `p2`.`player`
    ORDER BY `count` DESC
    

    live demo here

    【讨论】:

    • @diplosaurus 我会关注上面答案的前两句话
    • @strawberry 我同意,这似乎是最好的方法。另一个答案似乎有一个很好的规范化路线图,但是这个答案使用我拥有的(janky)模式回答了这个问题,所以我会接受它但实施另一个。
    【解决方案2】:

    我将从重构您的设计开始并介绍 3 个表格

    1) 播放器 其中将包含玩家数据及其唯一 ID

    CREATE TABLE players
        (`id` int, `name` varchar(255))
    ;
    INSERT INTO players
        (`id`,  `name`)
    VALUES
        (1, 'john'),
        (2, 'dave'),
        (3, 'mike'),
        (4, 'tim');
    

    2) 具有游戏数据及其唯一 ID 的游戏

    CREATE TABLE games
        (`id` int, `name` varchar(25))
    ;
    
    INSERT INTO games
        (`id`, `name`)
    VALUES
        (1001, 'G1'),
        (1002, 'G2'),
        (1003, 'G3'),
        (1004, 'G4');
    

    3) player_games 通过联结表将这 2 个实体关联为多对多关系,该联结表将根据您的示例数据保存游戏 ID 和玩家 ID

    CREATE TABLE player_games
        (`game_id` int, `player_id` int(11))
    ;
    
    INSERT INTO player_games
        (`game_id`, `player_id`)
    VALUES
        (1001, 1),
        (1001, 2),
        (1002, 1),
        (1002, 2),
        (1002, 3),
        (1002, 4),
        (1003, 3),
        (1003, 1),
        (1003, 2),
        (1004, 4),
        (1004, 2)
    ;
    

    谁参加的比赛最多?根据您玩了 4 场游戏的样本数据集,它不是约翰

    select t.games_played,group_concat(t.name) players
    from (
      select p.name,
      count(distinct pg.game_id) games_played
      from player_games pg
      join players p on p.id = pg.player_id
      group by p.name 
    ) t
    group by games_played
    order by games_played desc
    limit 1
    

    对于上述查询,有可能不止一个玩家玩过大多数游戏,比如 dave 玩了 4 游戏,而 tim 也玩了 4 游戏,所以两者都应该包括在内

    Demo

    哪对玩家一起玩的游戏最多? (约翰和戴夫)

    select t.games_played,group_concat(t.player_name) players
    from (
      select group_concat(distinct pg.game_id),
      concat(least(p.name, p1.name), ' ', greatest(p.name, p1.name)) player_name,
      count(distinct pg.game_id) games_played
      from player_games pg
      join player_games pg1 on pg.game_id = pg1.game_id
                           and pg.player_id <> pg1.player_id
      join players p on p.id = pg.player_id
      join players p1 on p1.id = pg1.player_id
      group by player_name
    ) t
    group by games_played
    order by games_played desc
    limit 1;
    

    在上面的查询中,我有自己加入的 player_games 表来获取每个游戏的玩家组合,然后为每个独特的配对分组数据,再次遵循相同的逻辑来处理可能有超过一对玩家玩过的机会大多数游戏

    Demo

    【讨论】:

    • 我不会打扰 group_concat 的东西,但其他方面很酷
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-09-22
    • 1970-01-01
    • 1970-01-01
    • 2022-09-27
    • 1970-01-01
    • 2012-10-27
    • 2016-12-28
    相关资源
    最近更新 更多