I have a table with the columns status, operator, and cost, where the columns status and operator are categorical and I want to sum up the cost per (status, operator) pair. Usually I would do this with a simple statement like
SELECT SUM(cost), status, operator FROM my_table GROUP BY status, operator;
but the hard part is there could be 100s of unique operators which I can't visualize for the client in a meaningful way. What I want to be able to do is only explicitly show the top N many operator categories (meaning to top N operators that have the highest SUM(cost) across the entire dataset) and then group all of the remaining rows in an "Other" operator. An inefficient way to do this would be the following:
-- letting N = 12
SELECT
SUM(cost),
status,
CASE
WHEN operator IN (
SELECT t.operator
FROM my_table AS t
GROUP BY t.operator ORDER BY SUM(t.cost) DESC
LIMIT 12
) THEN operator
ELSE 'Other'
END AS operator
FROM my_table
GROUP BY
status,
CASE
WHEN operator IN (
SELECT t.operator
FROM my_table AS t
GROUP BY t.operator ORDER BY SUM(t.cost) DESC
LIMIT 12
) THEN operator
ELSE 'Other'
END;
While the inefficient way works, in production it is far too slow. In actuality, the cost is not a simple column in a table but is computed by a subquery that is very slow to compute and the table is large, so I can't afford to use the CASE statement with an IN clause. What I would rather do is somehow have the full table where I use the GROUP BY statement I listed first in a FROM-clause subquery, then aggregate that to get the top N operator categories and an "Other" category. I tried to do this with window functions but I don't really understand how those work and I could not find something that got the right answer. If somebody could help it would be greatly appreciated.
EDIT: The cost column is not an actual column. I should have been more clear. It's computed by a very expensive subquery so I want to compute the cost for each row of the original table as few times as possible.
Example:
Say we have a table that looks like:
pk | status | operator | cost
----+-----------+-------------------+----------------------
1 | A | op_1 | 1
2 | A | op_1 | 5
3 | A | op_1 | 3
4 | A | op_1 | 7
5 | B | op_2 | 10
6 | B | op_2 | 15
7 | A | op_3 | 100
8 | A | op_4 | 1000
9 | B | op_5 | 12000
10 | A | op_5 | 10200
11 | B | op_5 | 10020
If I only want the top 3 operators (meaning the three operators with the highest SUM(cost) - in this case operators 3, 4, 5), the query should return:
status | operator | cost
-----------+-------------------+----------------------
B | op_5 | 32220
A | op_4 | 1000
A | op_3 | 100
B | Other | 25
A | Other | 16
In this example, operators 1-2 get rolled up into the "Other" operator, since we only want the top 3 given explicitly. So the first "Other" row in the result table sums all rows where status=B and operator is not one of the top three operators. The second "Other" row sums up all the rows where status=A and operator is not one of the top three operators.