Search code examples
mysqlsqlsequelpro

MySQL - Count number of rows in each group


I have a SQL table user_game which contains the games that a user owns:

| id | user_id | game_id |
|----|---------|---------|
| 83 | 1       | 1       |
| 84 | 1       | 2       |
| 85 | 1       | 3       |
| 86 | 2       | 2       |
| 87 | 2       | 3       |
| 88 | 2       | 4       |
| 89 | 3       | 2       |

I am trying to count the number of users which have 1 game, 2 games, 3 games.. etc.

User 1 has 3 games, User 2 has 3 games, and User 3 has 1 game. Therefore these are the results I want to achieve:

| no_of_games | COUNT(no_of_games) |
|-------------|--------------------|
| 1           | 1                  |
| 2           | 0                  |
| 3           | 2                  |

COUNT(no_of_games) is the number of users that have that number of games.


I can individually get the number of users for each no_of_games with this query:

-- Select no. of users with 1 game
SELECT no_of_games, COUNT(no_of_games)
FROM
(
  -- Select no. of games each user has
  SELECT user_id, COUNT(1) as no_of_games
  FROM user_game
  GROUP BY user_id
) as A
WHERE no_of_games = 1;

which gives the results:

| no_of_games | COUNT(no_of_games) |
|-------------|--------------------|
| 1           | 1                  |

However I have to change the no_of_games = 1 to 2, 3, 4... manually and UNION them with this solution and I can't do it for ~60 cases.

Is there a simpler way to achieve this?


Solution

  • Your problem is a bit tricky, because groups of games which do not appear in your data with a certain frequency (e.g. 2) will not appear in the result set just using your original table. In the query below, I use a second table called nums which simply contains the sequence 1 through 10 representing counts of number of games. By using a LEFT JOIN we can retain each game count in the final result set.

    SELECT t1.no_of_games,
           COALESCE(t2.no_of_games_count, 0) AS no_of_games_count
    FROM nums t1
    LEFT JOIN
    (
        SELECT t.no_of_games, COUNT(*) AS no_of_games_count
        FROM
        (
            SELECT COUNT(*) AS no_of_games
            FROM user_game
            GROUP BY user_id
        ) t
        GROUP BY t.no_of_games
    ) t2
        ON t1.no_of_games = t2.no_of_games
    ORDER BY t1.no_of_games
    

    And here is the definition I used for nums:

    CREATE TABLE nums (`no_of_games` int);
    INSERT INTO nums (`no_of_games`)
    VALUES
        (1),(2),(3),(4),(5),(6),(7),(8),(9),(10);
    

    Demo here:

    SQLFiddle