简体   繁体   中英

MySQL, sum up multiple rows and rank based on most votes

I have the following database

id  rank1   rank2   rank3   rank4
1   5       4       8       9
2   5       8       9       4
3   8       5       3       1
4   5       8       2       1
5   8       5       3       1
6   5       8       3       1

i need a mysql query or php script that will tally up the ranks and display the top 4 based on the number of times it appears in the table... ie. the end result should look something like:

rank1 = 5
rank2 = 8
rank3 = 3
rank4 = 1

any ideas??? thanks in advance

Your table design is far from optimal, if you didn't think it was before you will definitely see it after realizing that the way to get the result you are after requires this " not that pretty " query, though it works.

SELECT name, rank FROM (
  (
    SELECT 'rank1' name, rank1 rank
    FROM foobar GROUP BY rank1
    ORDER BY count(*) DESC LIMIT 1
  ) rank1_foobar
)
UNION SELECT name, rank FROM (
  (
    SELECT 'rank2' name, rank2 rank
    FROM foobar GROUP BY rank2
    ORDER BY count(*) DESC LIMIT 1
  ) rank2_foobar
)
UNION SELECT name, rank FROM (
  (
    SELECT 'rank3' name, rank3 rank
    FROM foobar GROUP BY rank3
    ORDER BY count(*) DESC LIMIT 1
  ) rank3_foobar
)
UNION SELECT name, rank FROM (
  (
    SELECT 'rank4' name, rank4 rank
    FROM foobar GROUP BY rank4
    ORDER BY count(*) DESC LIMIT 1
  ) rank4_foobar
)

output

+-------+------+
| name  | rank |
+-------+------+
| rank1 |    5 |
| rank2 |    8 |
| rank3 |    3 |
| rank4 |    1 |
+-------+------+

I would restructure your table into something as the below, that'd make it much easier to write queries as the one you've requested.

CREATE TABLE ranks (
  id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  group_id INT UNSIGNED NOT NULL COMMENT 'to be able to group more than one row in `ranks` together',
  rank_type ENUM('rank1','rank2','rank3','rank4'),
  rank_value INT,
  PRIMARY KEY(`id`)
);

Because of the poor data normalization, its not quite as simple as a single select/from group by. You need to query each "Rank" column as part of a union, THEN roll that up. To keep the interim temp summations down, we can still pre-group the counts so you are not running ALL rows 4 times, but the pre-roll-ups 1 per rank in the respective group segment

select
      PreAgg.Rank,
      SUM( PreAgg.RankCount ) as TotalCount
   from 
      ( select
              YT.Rank1 as Rank,
              COUNT(*) as RankCount
           from 
              YourTable YT
           group by 
              YT.Rank1
        UNION ALL
        select
              YT.Rank2 as Rank,
              COUNT(*) as RankCount
           from 
              YourTable YT
           group by 
              YT.Rank2
        UNION ALL
        select
              YT.Rank3 as Rank,
              COUNT(*) as RankCount
           from 
              YourTable YT
           group by 
              YT.Rank3
        UNION ALL
        select
              YT.Rank4 as Rank,
              COUNT(*) as RankCount
           from 
              YourTable YT
           group by 
              YT.Rank4 ) PreAgg
   GROUP BY
      PreAgg.Rank,
      SUM( PreAgg.RankCount ) DESC

As pointed out by Ajreal, and it would need more clarification to the structure... Is there a reason why you have 4 distinct columns that are all "Rank" instead of a more normalized table something like..

ID   RankGroup   Rank
1     1          5
2     1          5
3     1          8
4     1          5
5     1          8
6     1          5
7     2          4
7     2          8
7     2          5
7     2          8
7     2          5
7     2          8
etc for ranks 3 and 4

Then you could just get your counts per RANK regardless of the "group level" condition, or get best ranking per group in very simplified query.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM