Search code examples
mysqlsqlsubqueryranking

Ranking system using a complex query: more precise filters


To realize a ranking system on my gameserver, I store some information about each player in a MySQL database. I've got the following database tables.

Table players with example data:

id | steamId | deaths
---+---------+-------
1  | asdja   | 2
2  | kfjsl   | 5

Table weapons with example data:

playerId | weaponId | kills
---------+----------+------
1        | 5        | 8
1        | 9        | 7
2        | 3        | 3
2        | 6        | 10
2        | 7        | 2

You see that I do not store the kills for each player in the players-table, because I could simply calculate it from the weapons-table. I'm not very familiar with SQL queries, but I finished creating the following one to select a simple data set with following fields:

kills,
deaths,
kill-death-rate (kdrate),
count (maximum number of players),
rank (current ranking position, sorted by kills)

The query:

SELECT
    SUM(weapons.kills) AS `kills`,
    `deaths`,
    (SUM(kills) / IF(deaths, deaths, 1)) AS `kdrate`,
    (SELECT COUNT(*) FROM `players`) AS `count`,
    (
        SELECT
            COUNT(*)
        FROM
            (
                SELECT
                    p.id AS id2,
                    SUM(w.kills) AS kills2,
                    p.deaths AS deaths2,
                    p.steamId AS steamId2
                FROM
                    weapons AS w,
                    players AS p
                WHERE
                    p.id = w.playerId
                GROUP BY
                    p.id
            ) AS temp
        WHERE
            temp.kills2 >= (
                SELECT
                    SUM(weapons.kills) AS `kills`
                FROM
                    `players`,
                    `weapons`
                WHERE
                    players.id = weapons.playerId AND
                    `id` = 1
                GROUP BY
                  `id`
            )
        ORDER BY
            temp.kills2 DESC,
            temp.deaths2 ASC,
            temp.steamId2 ASC
    ) AS `rank`
FROM
    `players`
INNER JOIN
    `weapons`
ON
    players.id = weapons.playerId
WHERE
    `id` = 1
GROUP BY
    `id`

There are two problems:

1.) The query is horrible.

2.) Executing this query with the given example data results in "the same rank". I mean, both players get the same rank because I the amount of kills is the same on both players. But instead, player 1 should be on rank 1 because he has less deaths than player 2. I don't know how I can realize this.

Thanks in advance!

EDIT: @Manueru_mx gave me a basic idea of how to do it: I've got the following code based on his answer:

SELECT
    id,
    kills,
    deaths,
    kdrate,
    COUNT(*) AS rank,
    (SELECT COUNT(*) FROM players) AS `count`
FROM
    (
        SELECT
            pys.id AS id,
            SUM(wps.kills) AS kills,
            pys.deaths AS deaths,
            (SUM(wps.kills) / pys.deaths) as kdrate
        FROM
            players pys
        INNER JOIN
            weapons wps
        ON
            pys.id = wps.playerid
        GROUP BY
            pys.id
        ORDER BY
            2 DESC,
            3,
            4 ASC
    ) AS tmp
WHERE
    id = 1

The only problem left is, that the rank is 1 in both cases.


Solution

  • This is my "solution". This query is more simple but get the results you are looking

    select 
    pys.steamID, SUM(wps.kills) as Kills,
    SUM(pys.deaths) as Deaths,
    (sum(wps.kills)/sum(pys.deaths)) as kdratio
    --,COUNT(pys.steamID) as PlayersC
    from players pys
    inner join  weapons wps on pys.id = wps.playerid
    group by pys.steamID
    order by 2 DESC, 3, 4 ASC
    

    Add the rank.

    SELECT
        id,
        kills,
        deaths,
        kdrate,
        ranking_usr as rank,
        (SELECT COUNT(*) FROM players) AS `count`
    FROM
        (
            SELECT
                @row := @row + 1 AS ranking_usr
                pys.id AS id,
                SUM(wps.kills) AS kills,
                pys.deaths AS deaths,
                (SUM(wps.kills) / pys.deaths) as kdrate
            FROM
                players pys
            INNER JOIN
                weapons wps
            ON
                pys.id = wps.playerid
            GROUP BY
                pys.id
            ORDER BY
                2 DESC,
                3,
                4 ASC
        ) AS tmp
    WHERE
        id = 1