按组中的前n个(最小)项计数和分组

时间:2013-12-02 16:30:47

标签: mysql group-by

我已经浏览了几个“来自M的n”类型的解决方案,并且无法接近我所追求的内容,尽管之前可能已经问过其他格式的问题。

我已经尝试过这个MySQL Group By with top N number of each kindhttp://www.xaprb.com/blog/2006/12/07/how-to-select-the-firstleastmax-row-per-group-in-sql/的例子,这些例子似乎都不适用于我正在尝试做的事情。

我要做的是确定跑步比赛中最好的球队,个别跑步者不是问题,性别,年龄类别可以照顾。团队奖项的规则基于俱乐部的会员资格。

  1. 俱乐部必须至少有3名参赛者才有资格参加团体比赛。
  2. 每个俱乐部只有前三名选手参加比赛。
  3. 球队的位置取决于合格选手的总和,因此来自俱乐部A的选手将获得第2名和第9名选手。第10名得到21分,来自俱乐部B的选手,获得第4名,第5名和第5名。 6日获得15分等等。
  4. 我有一个包含以下字段的表:

    +---------------+-------------+------+-----+---------+----------------+
    | Field         | Type        | Null | Key | Default | Extra          |
    +---------------+-------------+------+-----+---------+----------------+
    | id            | int(11)     | NO   | PRI | NULL    | auto_increment |
    | runner_id     | int(11)     | YES  |     | NULL    |                |
    | club_id       | int(11)     | YES  |     | NULL    |                |
    | race_id       | int(11)     | YES  |     | NULL    |                |
    | race_number   | int(11)     | YES  |     | NULL    |                |
    | category      | varchar(20) | YES  |     | NULL    |                |
    | finish_time   | int(11)     | YES  |     | NULL    |                |
    | race_position | int(11)     | YES  |     | NULL    |                |
    +---------------+-------------+------+-----+---------+----------------+
    

    只有club_id和race_position与查询相关。 runner_id,club_id和race_id是外键,我需要能够在创建结果时从这些表中提取数据(given_name,family_name,age,club_name等)。

    这是典型数据:

    +----+-----------+---------+---------+-------------+-----------+-------------+---------------+
    | id | runner_id | club_id | race_id | race_number | category  | finish_time | race_position |
    +----+-----------+---------+---------+-------------+-----------+-------------+---------------+
    | 53 |        26 |       1 |      85 |          17 | Msenior   |        1666 |            11 |
    | 35 |        39 |       1 |      85 |           4 | Munder_18 |        1503 |             4 |
    | 63 |        61 |       2 |      85 |          27 | Mvet_50   |        1610 |             9 |
    | 42 |        46 |       2 |      85 |          11 | Lvet_40   |        1773 |            14 |
    | 38 |        42 |       2 |      85 |           7 | Lunder_18 |        1793 |            17 |
    | 56 |        36 |       9 |      85 |          20 | Msenior   |        1561 |             6 |
    | 44 |        48 |       9 |      85 |          13 | Msenior   |        1667 |            12 |
    | 64 |        62 |       9 |      85 |          28 | Msenior   |        1660 |            10 |
    | 49 |        52 |       9 |      85 |          18 | Msenior   |        1432 |             1 |
    | 47 |        51 |      10 |      85 |          16 | Msenior   |        1779 |            15 |
    | 61 |        59 |      11 |      85 |          25 | Mvet_50   |        1502 |             3 |
    | 33 |        38 |      11 |      85 |           2 | Munder_18 |        1440 |             2 |
    | 65 |        63 |      11 |      85 |          29 | Mvet_40   |        1566 |             8 |
    | 54 |        54 |      12 |      85 |          19 | Msenior   |        1785 |            16 |
    | 58 |        56 |      12 |      85 |          23 | Msenior   |        1546 |             5 |
    | 37 |        41 |      12 |      85 |           6 | Munder_18 |        1668 |            13 |
    | 45 |        49 |      14 |      85 |          14 | Mvet_50   |        1565 |             7 |
    +----+-----------+---------+---------+-------------+-----------+-------------+---------------+
    

    我想要最终得到的是:

    +----+-----------+---------+---------+-------------+-----------+-------------+---------------+
    | id | runner_id | club_id | race_id | race_number | category  | finish_time | race_position |
    +----+-----------+---------+---------+-------------+-----------+-------------+---------------+
    | 33 |        38 |      11 |      85 |           2 | Munder_18 |        1440 |             2 |
    | 61 |        59 |      11 |      85 |          25 | Mvet_50   |        1502 |             3 |
    | 65 |        63 |      11 |      85 |          29 | Mvet_40   |        1566 |             8 |
    | 49 |        52 |       9 |      85 |          18 | Msenior   |        1432 |             1 |
    | 56 |        36 |       9 |      85 |          20 | Msenior   |        1561 |             6 |
    | 64 |        62 |       9 |      85 |          28 | Msenior   |        1660 |            10 |
    | 58 |        56 |      12 |      85 |          23 | Msenior   |        1546 |             5 |
    | 37 |        41 |      12 |      85 |           6 | Munder_18 |        1668 |            13 |
    | 54 |        54 |      12 |      85 |          19 | Msenior   |        1785 |            16 |
    | 63 |        61 |       2 |      85 |          27 | Mvet_50   |        1610 |             9 |
    | 42 |        46 |       2 |      85 |          11 | Lvet_40   |        1773 |            14 |
    | 38 |        42 |       2 |      85 |           7 | Lunder_18 |        1793 |            17 |
    +----+-----------+---------+---------+-------------+-----------+-------------+---------------+
    

    所以尽管52岁的runner_id赢得了比赛,但他并没有参加胜利的球队。

    我在Codeigniter / Datamapper ORM下运行所有​​这些,但是我可以通过这一层向下传递一个完整的SQL查询字符串。

    我希望这一切都有道理。

2 个答案:

答案 0 :(得分:0)

MySQL缺乏重要的功能来解决这个问题(CTE,窗口函数),但是您可以使用一些用户定义的变量并通过支付性能成本来解决它们:

SELECT s1.id, s1.runner_id, s1.club_id, s1.race_id, s1.race_number, s1.category,
  s1.finish_time, s1.race_position
FROM (
  SELECT t1.*,
    @club_rank := if(@prev_club = t1.club_id, @club_rank + 1, 1) club_rank,
    @prev_club := t1.club_id
  FROM t t1
  CROSS JOIN (SELECT @prev_club := NULL, @club_rank := 1) init
  ORDER BY t1.club_id, t1.race_position
) s1
JOIN (
  SELECT club_id, count(*) teamSize, sum(race_position) teamPosition FROM t
  GROUP BY club_id
) s2 ON s1.club_id = s2.club_id
WHERE club_rank <= 3 AND teamSize >= 3
ORDER BY teamPosition, race_position

输出:

| ID | RUNNER_ID | CLUB_ID | RACE_ID | RACE_NUMBER |  CATEGORY | FINISH_TIME | RACE_POSITION |
|----|-----------|---------|---------|-------------|-----------|-------------|---------------|
| 33 |        38 |      11 |      85 |           2 | Munder_18 |        1440 |             2 |
| 61 |        59 |      11 |      85 |          25 |   Mvet_50 |        1502 |             3 |
| 65 |        63 |      11 |      85 |          29 |   Mvet_40 |        1566 |             8 |
| 49 |        52 |       9 |      85 |          18 |   Msenior |        1432 |             1 |
| 56 |        36 |       9 |      85 |          20 |   Msenior |        1561 |             6 |
| 64 |        62 |       9 |      85 |          28 |   Msenior |        1660 |            10 |
| 58 |        56 |      12 |      85 |          23 |   Msenior |        1546 |             5 |
| 37 |        41 |      12 |      85 |           6 | Munder_18 |        1668 |            13 |
| 54 |        54 |      12 |      85 |          19 |   Msenior |        1785 |            16 |
| 63 |        61 |       2 |      85 |          27 |   Mvet_50 |        1610 |             9 |
| 42 |        46 |       2 |      85 |          11 |   Lvet_40 |        1773 |            14 |
| 38 |        42 |       2 |      85 |           7 | Lunder_18 |        1793 |            17 |

小提琴here

答案 1 :(得分:0)

有点迟,因为我不适应。

我想出了一个不太优雅的解决方案。我在表格中添加了一个club_total列。然后我循环遍历表格,每个俱乐部都有一个查询,获得前N个参赛者的查询,如:

select * from entries where race_id=? and club_id=? LIMIT ? order by race_position;

然后我忽略了那些少于N个终结者的俱乐部并将其他俱乐部的比赛位置相加并将这个值写回到桌面。

最后,我运行另一个查询,只提取那些带有总计数的行:

select * from entries where club_total > 0 and race_id=? order by club_total, race_position;

就像我说的那样,它并不优雅,而且肯定不会很快(我没有及时计算)但它只能在一台机器上每年运行几次,并且记录集是几百行一个最大值。使用小数据集时,它不会比通过AJAX显示数据的简单查询慢得多。在这种情况下完成工作比速度更重要。我不会将这种方法用于性能问题的任何情况