documentation/cookbook/sql/advanced/top-n-plus-others.md
Create aggregated results showing the top N items individually, with all remaining items combined into a single "Others" row. This pattern is useful for dashboards and reports where you want to highlight the most important items while still showing the total.
You want to display results like:
| symbol | total_trades |
|---|---|
| BTC-USDT | 15234 |
| ETH-USDT | 12890 |
| SOL-USDT | 8945 |
| MATIC-USDT | 6723 |
| AVAX-USDT | 5891 |
| -Others- | 23456 |
Instead of listing all symbols (which might be thousands), show the top 5 individually and aggregate the rest.
Use rank() to identify top N rows, then use CASE to group remaining rows:
WITH totals AS (
SELECT
symbol,
count() as total
FROM trades
WHERE timestamp IN '$now - 1d..$now'
),
ranked AS (
SELECT
*,
rank() OVER (ORDER BY total DESC) as ranking
FROM totals
)
SELECT
CASE
WHEN ranking <= 5 THEN symbol
ELSE '-Others-'
END as symbol,
SUM(total) as total_trades
FROM ranked
GROUP BY 1
ORDER BY total_trades DESC;
Results:
| symbol | total_trades |
|---|---|
| BTC-USDT | 15234 |
| ETH-USDT | 12890 |
| SOL-USDT | 8945 |
| MATIC-USDT | 6723 |
| AVAX-USDT | 5891 |
| -Others- | 23456 |
The query uses a three-step approach:
Aggregate data (totals CTE):
Rank rows (ranked CTE):
rank() OVER (ORDER BY total DESC): Assigns rank based on count (1 = highest)Conditional grouping (outer query):
CASE WHEN ranking <= 5: Keep top 5 with original namesELSE '-Others-': Rename all others to "-Others-"SUM(total): Aggregate counts (combines all "Others" into one row)GROUP BY 1: Group by the CASE expression resultrank() assigns ranks with gaps for ties:
| symbol | total | rank |
|---|---|---|
| BTC-USDT | 1000 | 1 |
| ETH-USDT | 900 | 2 |
| SOL-USDT | 900 | 2 |
| AVAX-USDT | 800 | 4 |
| MATIC-USDT | 700 | 5 |
If there are ties at the boundary (rank 5), all tied items will be included in top N.
If you don't want to handle ties and always want exactly N rows in top tier:
WITH totals AS (
SELECT symbol, count() as total
FROM trades
),
ranked AS (
SELECT *, row_number() OVER (ORDER BY total DESC) as rn
FROM totals
)
SELECT
CASE WHEN rn <= 5 THEN symbol ELSE '-Others-' END as symbol,
SUM(total) as total_trades
FROM ranked
GROUP BY 1
ORDER BY total_trades DESC;
Difference:
rank(): May include more than N if there are ties at position Nrow_number(): Always exactly N in top tier (breaks ties arbitrarily)Different top N:
-- Top 10 instead of top 5
WHEN ranking <= 10 THEN symbol
-- Top 3
WHEN ranking <= 3 THEN symbol
Different aggregations:
-- Sum instead of count
WITH totals AS (
SELECT symbol, SUM(amount) as total_volume
FROM trades
)
...
Multiple levels:
SELECT
CASE
WHEN ranking <= 5 THEN symbol
WHEN ranking <= 10 THEN '-Top 10-'
ELSE '-Others-'
END as category,
SUM(total) as count
FROM ranked
GROUP BY 1;
Results in three groups: top 5 individual, ranks 6-10 combined, rest combined.
With percentage:
WITH totals AS (
SELECT symbol, count() as total
FROM trades
WHERE timestamp IN '$now - 1d..$now'
),
ranked AS (
SELECT *, rank() OVER (ORDER BY total DESC) as ranking
FROM totals
),
summed AS (
SELECT SUM(total) as grand_total FROM totals
),
grouped AS (
SELECT
CASE WHEN ranking <= 5 THEN symbol ELSE '-Others-' END as symbol,
SUM(total) as total_trades
FROM ranked
GROUP BY 1
)
SELECT
symbol,
total_trades,
round(100.0 * total_trades / grand_total, 2) as percentage
FROM grouped CROSS JOIN summed
ORDER BY total_trades DESC;
Show top N for multiple dimensions:
WITH totals AS (
SELECT
symbol,
side,
count() as total
FROM trades
WHERE timestamp IN '$now - 1d..$now'
),
ranked AS (
SELECT
*,
rank() OVER (PARTITION BY side ORDER BY total DESC) as ranking
FROM totals
)
SELECT
side,
CASE WHEN ranking <= 3 THEN symbol ELSE '-Others-' END as symbol,
SUM(total) as total_trades
FROM ranked
GROUP BY side, 2
ORDER BY side, total_trades DESC;
This shows top 3 symbols separately for buy and sell sides.
This pattern is particularly useful for charts:
Pie/Donut charts:
-- Top 5 slices plus "Others" slice
CASE WHEN ranking <= 5 THEN symbol ELSE '-Others-' END
Bar charts:
-- Top 10 bars, sorted by value
CASE WHEN ranking <= 10 THEN symbol ELSE '-Others-' END
ORDER BY total_trades DESC
:::warning Empty Others Row If there are N or fewer distinct values, the "Others" row won't appear (or will have 0 count). Handle this in your visualization logic if needed. :::
:::info Related Documentation