Question

I need to query predefined sets of skill ratings out of a PostgreSQL table. Consider the following demo table with users 1, 2 and 3, each of which has different skills and skill ratings:

╔════════╦═══════════╦═══════╗
║ userId ║  skillId  ║ votes ║
╠════════╬═══════════╬═══════╣
║      1 ║ marketing ║    50 ║
║      2 ║ hacking   ║    51 ║
║      1 ║ business  ║    59 ║
║      2 ║ business  ║     8 ║
║      1 ║ hacking   ║    32 ║
║      3 ║ talking   ║    67 ║
║      3 ║ business  ║    34 ║
║      3 ║ marketing ║    24 ║
╚════════╩═══════════╩═══════╝

Now I would like to rank them according to weighted business+hacking:

SELECT * FROM
  (SELECT
    b."userId",
    MAX(CASE WHEN b."skillId"='business' THEN b.votes ELSE 0 END) AS "businessVotes",
    MAX(CASE WHEN h."skillId"='hacking' THEN h.votes ELSE 0 END) AS "hackingVotes"
    FROM "Skill" b, "Skill" h
    WHERE b."userId"=h."userId"
    AND (
      (b."skillId"='business' AND h."skillId"='hacking') OR
      (b."skillId"=h."skillId" AND (b."skillId"='business' OR b."skillId"='hacking'))
    )
    GROUP BY b."userId") AS query
  ORDER BY "businessVotes"*0.2+"hackingVotes"*0.8 DESC

As expected I get the following result:

╔════════╦═══════════════╦══════════════╗
║ userId ║ businessVotes ║ hackingVotes ║
╠════════╬═══════════════╬══════════════╣
║      2 ║             8 ║           51 ║
║      1 ║            59 ║           32 ║
║      3 ║            34 ║            0 ║
╚════════╩═══════════════╩══════════════╝

But it seems to me this query requires PostgreSQL to do more computation than necessary because of intricately handling the case where user do not have hacking but business skills or the other way round. Without this case (i.e. both business and hacking skills are required to get into the ranking) the query could be a lot easier:

SELECT *
  FROM "Skill" t1, "Skill" t2
  WHERE t1."userId"=t2."userId"
  AND (t1."skillId"='business'
  AND t2."skillId"='hacking')
  ORDER BY t1."votes"*0.2 + t2."votes"*0.8 DESC;

Is it because of an error in my reasoning, a disadvantageous table structure or just because it is the way it is and there is no better alternative?

Was it helpful?

Solution

You can do this with an aggregation query:

select s.userId,
       sum((case when s.skillId = 'business' then votes else 0 end)*0.2 +
           (case when s.skillId = 'hacking' then votes else 0 end)*0.8
          ) as weightedpoints
from skill s
group by userId
order by weightedpoints desc
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top