Search code examples
sqlperformancepostgresqlranking

PostgreSQL ranking and related


I have a table which stores userids and their points in different categories. The number of users will keep changing. The points will also keep changing (like the points in stackoverflow). So a user who logins in will see a dashboard which says, for each of the 3 categories - You have 850 points, there are 950 users ahead of you. This is the query I have now -

WITH USERS AS (
   SELECT COUNT(*) TOT 
   FROM user_pointS
) 
SELECT ' You have ' || points_cat1 ||' points and there are '|| tot-rnk || ' ahead of you '   
FROM (
    SELECT ID,  
           points_cat1, 
           rank() OVER (ORDER BY  points_cat1 DESC ) AS RNK 
    FROM user_pointS
  ) AS RANKED, 
    USERS
WHERE ID = 10

Is there a better way (performance-wise)? I will have to repeat this for the 3 columns?


Solution

  • Well, you can do this without the CTE:

    SELECT ' You have ' || points_cat1 ||' points and there are '|| tot-rnk || ' ahead of you '   
    FROM (SELECT ID, points_cat1, 
                  rank() OVER (ORDER BY points_cat1 DESC ) AS RNK ,
                  count(*) over () as TOT
          FROM user_pointS
         ) RANKED
    WHERE ID = 10;
    

    You can do it for all three catagories at once:

    SELECT ' You have ' || points_cat1 ||' points and there are '|| tot-rnk1 || ' ahead of you ',
           ' You have ' || points_cat2 ||' points and there are '|| tot-rnk2 || ' ahead of you ',
           ' You have ' || points_cat3 ||' points and there are '|| tot-rnk3 || ' ahead of you '
    
    FROM (SELECT ID, points_cat1, points_cat2, points_cat3,
                  rank() OVER (ORDER BY points_cat1 DESC ) AS RNK ,
                  rank() OVER (ORDER BY points_cat2 DESC ) AS RNK1 ,
                  rank() OVER (ORDER BY points_cat3 DESC ) AS RNK2 ,
                  count(*) over () as TOT
          FROM user_pointS
         ) RANKED
    WHERE ID = 10;
    

    You can probably replace the tot-rnk with an inverse ranking:

     rank() OVER (ORDER BY points_cat1 ASC ) AS RNK
    

    but you would want to test that to be sure it gives you the results you expect.