I have three tables. Changes, Discussions and Conversations. Each Discussion has a collection of Conversations. Each Conversation is about a Change. The Conversations are then ranked 1-10 inside of the Discussion. You would end up with something like this:

  Conversations{
    [ id: 1, discussion_id: 1, change_id 1, rank: 1 ],
    [ id: 1, discussion_id: 1, change_id 3, rank: 3 ],
    [ id: 1, discussion_id: 1, change_id 5, rank: 2 ],
    [ id: 1, discussion_id: 1, change_id 2, rank: 4 ],
    [ id: 1, discussion_id: 1, change_id 4, rank: 5 ],
    [ id: 1, discussion_id: 2, change_id 3, rank: 1 ],
    [ id: 1, discussion_id: 2, change_id 2, rank: 2 ],
    [ id: 1, discussion_id: 2, change_id 4, rank: 5 ],
    [ id: 1, discussion_id: 2, change_id 5, rank: 3 ],
    [ id: 1, discussion_id: 2, change_id 1, rank: 4 ]
  }

I want to find the highest scoring change. I thought I had done that when I came up with this query:

  SELECT changes.*, SUM(conversations.rank) AS score FROM "changes" 
        INNER JOIN "conversations" ON "conversations"."change_id" = "changes"."id" 
        GROUP BY changes.id

This adds up all the ranks. It would mean that lower is better. However, this rather primitive solution has its flaws: If you give one change a rank of 1 and 3 it will end up with the same score as if you rank one change with 4.

Perhaps to fix this you could make it so higher is better: 1st place = 5 points, 2nd = 4 points, ect. I wouldn't know how to implement this. It also may not be the best way to do it.

Here you can find an SQL fiddle for context. Just an FYI: I'm doing this with active record. I generated the query using this:

Change.select("changes.*, SUM(conversations.rank) as conversations_rank").group("changes.id").joins(:conversations)
有帮助吗?

解决方案

I figured out two ways of doing it. Both very simple. I was just being a tad silly :)

The first, and probably most simple one:

SELECT changes.name, SUM(11 - conversations.rank) as score FROM "changes" 
      INNER JOIN "conversations" 
        ON "conversations"."change_id" = "changes"."id" 
      GROUP BY changes.id 
      ORDER BY score desc

Simple maths eh. So if conversations.rank = 1, then it will be 10, because 1 minus 11 is 10.

Perhaps the better way to do it is to have another table:

CREATE TABLE ranks (
  rank INT UNSIGNED PRIMARY KEY, 
  value INT UNSIGNED
);

INSERT INTO ranks 
       VALUES (1, 5),
              (2, 4),
              (3, 3),
              (4, 2),
              (5, 1);

And then to do a join ranks on conversations.rank:

SELECT changes.name, SUM(ranks.value) as score FROM "changes"
      INNER JOIN "conversations"
        ON conversations.change_id = changes.id
      INNER JOIN "ranks"
        ON conversations.rank = r.rank
      GROUP BY changes.name;

Thanks for all the help.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top