views:

116

answers:

2

<hyperbole>Whoever answers this question can claim credit for solving the world's most challenging SQL query, according to yours truly.</hyperbole>

Working with 3 tables: users, badges, awards.

Relationships: user has many awards; award belongs to user; badge has many awards; award belongs to badge. So badge_id and user_id are foreign keys in the awards table.

The business logic at work here is that every time a user wins a badge, he/she receives it as an award. A user can be awarded the same badge multiple times. Each badge is assigned a designated point value (point_value is a field in the badges table). For example, BadgeA can be worth 500 Points, BadgeB 1000 Points, and so on. As further example, let's say UserX won BadgeA 10 times and BadgeB 5 times. BadgeA being worth 500 Points, and BadgeB being worth 1000 Points, UserX has accumulated a total of 10,000 Points ((10 x 500) + (5 x 1000)).

The end game here is to return a list of top 50 users who have accumulated the most badge points.

Can you do it?

+5  A: 

My sample tables are:

user:

+-------+--------------+------+-----+---------+-------+
| Field | Type         | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| id    | int(11)      | YES  |     | NULL    |       |
| name  | varchar(200) | YES  |     | NULL    |       |
+-------+--------------+------+-----+---------+-------+

badge:

+-------+---------+------+-----+---------+-------+
| Field | Type    | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id    | int(11) | YES  |     | NULL    |       |
| score | int(11) | YES  |     | NULL    |       |
+-------+---------+------+-----+---------+-------+

award:

+----------+---------+------+-----+---------+-------+
| Field    | Type    | Null | Key | Default | Extra |
+----------+---------+------+-----+---------+-------+
| id       | int(11) | YES  |     | NULL    |       |
| user_id  | int(11) | YES  |     | NULL    |       |
| badge_id | int(11) | YES  |     | NULL    |       |
+----------+---------+------+-----+---------+-------+

Thus the query is:

SELECT user.name, SUM(score)
  FROM badge JOIN award ON badge.id = award.badge_id
       JOIN user ON user.id = award.user_id
 GROUP BY user.name
 ORDER BY 2
 LIMIT 50
Spidey
I think the asker is interested in both users and the score.
Hamish Grubijan
Right. fixed :)
Spidey
what does the order by 2 do?
keruilin
'ORDER BY 2' means order by the 2nd field we've selected. It's especially helpful when one of your SELECT items is a long series of actions on one or more fields. Oh, and don't forget to ORDER BY 2 DESC, because I'm guessing the default is ASCending.
Spidey
+4  A: 

No, that's not the worlds most challenging query. Something simple like this should do it:

select u.id, u.name, sum(b.points) as Points
from users u
inner join awards a on a.user_id = u.id
inner join badges b on b.id = a.badge_id
group by u.id, u.name
order by 2 desc
limit 50
Guffa