Recipe Tableau and SQL

How do I show the top N in each category, with the rest as Other?

Rank inside each category, not across the whole table: RANK_UNIQUE(SUM([revenue]), 'desc') restarting every category in Tableau, ROW_NUMBER() OVER (PARTITION BY category ...) in SQL. Then label anything past N as Other. Keep the Other bar: in this bookshop, the top 3 fiction titles make only 12.8% of fiction sales.

Updated

The data

Larkspur Books, sales in 2025

Synthetic datasales.csv

An independent bookshop with five sections: lots of fiction that sells steadily, one runaway cookbook, picture books with two exact ties.

sale_datecategorytitlecopiesrevenue
2025-01-01Children'sCount the Clouds112
2025-01-01FictionBlue Hour114
2025-01-01FictionStillwater116
2025-01-01FictionThe Orchardist's Daughter115

Step 1

Total every title

74 titles, far too many for one chart. One cookbook towers over everything; fiction is a long, flat run of similar bars.

Tableau

SUM([revenue])

SQL

SELECT category, title, SUM(revenue) AS revenue
FROM input
GROUP BY category, title
ORDER BY revenue DESC, title

Rows

CategoryTitleRevenue
CookbooksOne Pan Everything$5,824
Popular scienceThe Hidden Life of Fungi$1,344
Children'sThe Very Brave Badger$1,104
Popular scienceWhy Clocks Tick$1,012
Children'sMoon Snail's Big Day$972
Popular scienceOcean in a Drop$902
TravelWalking the Coast Path$902
CookbooksSourdough at Home$840
Children'sPip and the Puddle$770
Children'sTen Sleepy Owls$770
FictionStillwater$672
CookbooksFermented$660
FictionThe Last Cartographer$630
FictionThe Tin Crown$630
FictionThe Clockmaker's Wife$615
FictionThe Orchardist's Daughter$615
FictionThe Night Gardener$608
FictionEvery Other Summer$602
FictionA House Called Tomorrow$600
FictionNorth of Nowhere$592
FictionSmall Hours$592
CookbooksThe Baking Year$578
FictionA Map of Small Lies$576
FictionBlue Hour$574
FictionUnder the Linden$570
FictionHarbour Lights$555
FictionBright Hollow$544
FictionThe Weaving Room$525
FictionWhat the River Kept$512
FictionThe Glass Ferry$510
FictionThe Lantern Keeper$510
CookbooksSmoke and Fire$504
FictionPaper Moons$496
FictionWinter of the Heron$495
FictionThe Salt Orchard$490
Popular scienceHow Bridges Stand$480
FictionFolded Letters$476
CookbooksSalt Fat Sunday$476
Children'sMabel's Map$468
CookbooksSoup for Every Season$468
FictionAsh and Juniper$465
CookbooksBreakfast All Day$442
FictionThe Ninth Wave$435
FictionThe Quiet Season$420
FictionThe Long Table$392
FictionThe Second Mrs. Ames$392
CookbooksThe Weeknight Table$390
Popular scienceA Short History of Salt$380
CookbooksLittle Plates$364
TravelIslands of Scotland$360

First 50 of 74 rows.

Step 2

Try the Top 10 filter

The obvious move, and it hides the shop's biggest section. Fiction sells the most of any section, yet not one fiction title makes the overall top 10: its best seller ranks 11th. A Top N filter ranks across the whole view before any category splits it.

Tableau

Filter on title: Top 10 by SUM([revenue])

SQL

SELECT category, title, SUM(revenue) AS revenue
FROM input
GROUP BY category, title
ORDER BY revenue DESC, title
LIMIT 10

Rows

CategoryTitleRevenue
CookbooksOne Pan Everything$5,824
Popular scienceThe Hidden Life of Fungi$1,344
Children'sThe Very Brave Badger$1,104
Popular scienceWhy Clocks Tick$1,012
Children'sMoon Snail's Big Day$972
Popular scienceOcean in a Drop$902
TravelWalking the Coast Path$902
CookbooksSourdough at Home$840
Children'sPip and the Puddle$770
Children'sTen Sleepy Owls$770

Common mistake

A Top N filter across the whole view

A Top N filter or a plain LIMIT picks the best N titles in the shop, then splits them by section. Sections with many mid-sized sellers vanish. Rank inside each partition and filter on that rank.

WrongStarts with Cookbooks, Popular science, Children's…
Point at the line to read it.
$0$2K$4KCategories in this order →

Tableau

Filter on title: Top 3 by SUM([revenue])

SQL

SELECT category, title, SUM(revenue) AS revenue
FROM input
GROUP BY category, title
ORDER BY revenue DESC
LIMIT 15
RightStarts with Children's, Children's, Children's…
Point at the line to read it.
$0$2K$4KCategories in this order →

Tableau

RANK_UNIQUE(SUM([revenue]), 'desc') <= [Top N]
// on Filters, True; Compute Using: title

SQL

SELECT category, title, SUM(revenue) AS revenue
FROM input
GROUP BY category, title
QUALIFY ROW_NUMBER() OVER (PARTITION BY category ORDER BY SUM(revenue) DESC, title) <= 3
ORDER BY category, revenue DESC

Step 3

Rank inside each section

Now every section has its own number one. The key is Compute Using: along title, with category left out, so the rank restarts at 1 in each section. With Table (down) it would rank across all 74 titles again.

Tableau

RANK_UNIQUE(SUM([revenue]), 'desc')
// Compute Using: title, so it restarts for every category

SQL

SELECT
  category,
  title,
  SUM(revenue) AS revenue,
  ROW_NUMBER() OVER (PARTITION BY category ORDER BY SUM(revenue) DESC, title) AS rank_in_category
FROM input
GROUP BY category, title
ORDER BY category, rank_in_category

Rows

CategoryTitleRevenueRank in category
Children'sThe Very Brave Badger$1,1041
Children'sMoon Snail's Big Day$9722
Children'sPip and the Puddle$7703
Children'sTen Sleepy Owls$7704
Children'sMabel's Map$4685
Children'sCount the Clouds$3366
Children'sThe Dragon Who Hated Tea$2727
Children'sA Hat for Hedgehog$2618
Children'sRocket Ruth$2509
Children'sBusy Diggers$23210
Children'sWhere Do Whales Sleep?$20711
Children'sZoe and the Zebra Bus$20412
Children'sThe Muddy Puddle Club$19813
Children'sBedtime on Mars$18014
Children'sThe Sock Monster$18015
Children'sGoodnight, Lighthouse$16816
CookbooksOne Pan Everything$5,8241
CookbooksSourdough at Home$8402
CookbooksFermented$6603
CookbooksThe Baking Year$5784
CookbooksSmoke and Fire$5045
CookbooksSalt Fat Sunday$4766
CookbooksSoup for Every Season$4687
CookbooksBreakfast All Day$4428
CookbooksThe Weeknight Table$3909
CookbooksLittle Plates$36410
CookbooksVegetable Kingdom Kitchen$30811
CookbooksNoodle Nights$27012
FictionStillwater$6721
FictionThe Last Cartographer$6302
FictionThe Tin Crown$6303
FictionThe Clockmaker's Wife$6154
FictionThe Orchardist's Daughter$6155
FictionThe Night Gardener$6086
FictionEvery Other Summer$6027
FictionA House Called Tomorrow$6008
FictionNorth of Nowhere$5929
FictionSmall Hours$59210
FictionA Map of Small Lies$57611
FictionBlue Hour$57412
FictionUnder the Linden$57013
FictionHarbour Lights$55514
FictionBright Hollow$54415
FictionThe Weaving Room$52516
FictionWhat the River Kept$51217
FictionThe Glass Ferry$51018
FictionThe Lantern Keeper$51019
FictionPaper Moons$49620
FictionWinter of the Heron$49521
FictionThe Salt Orchard$49022

First 50 of 74 rows.

Step 4

Label the top N, call the rest Other

Each title now knows which side of the line it's on. Move the slider: the line moves inside every section at once.

Tableau

IF RANK_UNIQUE(SUM([revenue]), 'desc') <= [Top N] THEN ATTR([title])
ELSE "Other"
END
// [Top N] is an integer parameter

SQL

SELECT
  category,
  title,
  SUM(revenue) AS revenue,
  CASE
    WHEN ROW_NUMBER() OVER (PARTITION BY category ORDER BY SUM(revenue) DESC, title) <= 3 THEN 'Top {knob.n}'
    ELSE 'Other'
    END AS bucket
FROM input
GROUP BY category, title
ORDER BY category, revenue DESC, title

Rows

CategoryTitleRevenueBucket
CookbooksOne Pan Everything$5,824Top 3
Popular scienceThe Hidden Life of Fungi$1,344Top 3
Children'sThe Very Brave Badger$1,104Top 3
Popular scienceWhy Clocks Tick$1,012Top 3
Children'sMoon Snail's Big Day$972Top 3
Popular scienceOcean in a Drop$902Top 3
TravelWalking the Coast Path$902Top 3
CookbooksSourdough at Home$840Top 3
Children'sPip and the Puddle$770Top 3
Children'sTen Sleepy Owls$770Other
FictionStillwater$672Top 3
CookbooksFermented$660Top 3
FictionThe Last Cartographer$630Top 3
FictionThe Tin Crown$630Top 3
FictionThe Clockmaker's Wife$615Other
FictionThe Orchardist's Daughter$615Other
FictionThe Night Gardener$608Other
FictionEvery Other Summer$602Other
FictionA House Called Tomorrow$600Other
FictionNorth of Nowhere$592Other
FictionSmall Hours$592Other
CookbooksThe Baking Year$578Other
FictionA Map of Small Lies$576Other
FictionBlue Hour$574Other
FictionUnder the Linden$570Other
FictionHarbour Lights$555Other
FictionBright Hollow$544Other
FictionThe Weaving Room$525Other
FictionWhat the River Kept$512Other
FictionThe Glass Ferry$510Other
FictionThe Lantern Keeper$510Other
CookbooksSmoke and Fire$504Other
FictionPaper Moons$496Other
FictionWinter of the Heron$495Other
FictionThe Salt Orchard$490Other
Popular scienceHow Bridges Stand$480Other
CookbooksSalt Fat Sunday$476Other
FictionFolded Letters$476Other
Children'sMabel's Map$468Other
CookbooksSoup for Every Season$468Other
FictionAsh and Juniper$465Other
CookbooksBreakfast All Day$442Other
FictionThe Ninth Wave$435Other
FictionThe Quiet Season$420Other
FictionThe Long Table$392Other
FictionThe Second Mrs. Ames$392Other
CookbooksThe Weeknight Table$390Other
Popular scienceA Short History of Salt$380Other
CookbooksLittle Plates$364Other
Popular scienceThe Body Electric$360Other

First 50 of 74 rows.

18

Step 5

Fold the tail into one Other bar

One bar per section, the top N stacked on the tail. Sort by the rank, not by revenue, so Other always sits last. Tableau stacks one mark per title in the same colour; SQL folds them into a single row.

Tableau

IF RANK_UNIQUE(SUM([revenue]), 'desc') <= [Top N] THEN "Top N"
ELSE "Other"
END
// on Color; category on Columns, stacked bars

SQL

WITH ranked AS (
  SELECT
    category,
    title,
    SUM(revenue) AS revenue,
    ROW_NUMBER() OVER (PARTITION BY category ORDER BY SUM(revenue) DESC, title) AS r
  FROM input
  GROUP BY category, title
)
SELECT
  category,
  CASE WHEN r <= 3 THEN title ELSE 'Other' END AS label,
  CASE
    WHEN r <= 3 THEN 'Top {knob.n}'
    ELSE 'Other'
    END AS bucket,
  SUM(revenue) AS revenue,
  MIN(r) AS sort_order
FROM ranked
GROUP BY ALL
ORDER BY category, sort_order

Rows

CategoryLabelBucketRevenueR
Children'sThe Very Brave BadgerTop 3$1,1041
CookbooksOne Pan EverythingTop 3$5,8241
FictionStillwaterTop 3$6721
Popular scienceThe Hidden Life of FungiTop 3$1,3441
TravelWalking the Coast PathTop 3$9021
Children'sMoon Snail's Big DayTop 3$9722
CookbooksSourdough at HomeTop 3$8402
FictionThe Last CartographerTop 3$6302
Popular scienceWhy Clocks TickTop 3$1,0122
TravelIslands of ScotlandTop 3$3602
Children'sPip and the PuddleTop 3$7703
CookbooksFermentedTop 3$6603
FictionThe Tin CrownTop 3$6303
Popular scienceOcean in a DropTop 3$9023
TravelThe Road to MarrakechTop 3$3363
Children'sOtherOther$3,7264
CookbooksOtherOther$3,8004
FictionOtherOther$13,1614
Popular scienceOtherOther$2,3124
TravelOtherOther$1,1904

Why keep the Other bar?

Because the tail can be most of the business. At N = 3, Other holds 87.2% of fiction sales and 34.2% of cookbook sales. A "top 3 per section" chart without it says the cookbook section is one book, which is nearly true, and says fiction is three books, which is badly wrong: fiction is a long run of steady sellers, none of them a hit. Drop Other and that difference disappears.

Common mistake

Sorting the Other row by revenue

Other is a sum of many titles, so it is often the biggest row. Here fiction's Other bar is larger than the runaway cookbook. Sort by revenue and Other jumps to the top of the list; sort by the rank and it stays last.

WrongStarts with Children's, Children's, Children's…
Point at the line to read it.
$0$5K$10KCategories in this order →

SQL

WITH ranked AS (
  SELECT
    category,
    title,
    SUM(revenue) AS revenue,
    ROW_NUMBER() OVER (PARTITION BY category ORDER BY SUM(revenue) DESC, title) AS r
  FROM input
  GROUP BY category, title
)
SELECT
  category,
  CASE WHEN r <= 3 THEN title ELSE 'Other' END AS label,
  SUM(revenue) AS revenue
FROM ranked
GROUP BY ALL
ORDER BY category, revenue DESC
RightStarts with Children's, Children's, Children's…
Point at the line to read it.
$0$5K$10KCategories in this order →

SQL

WITH ranked AS (
  SELECT
    category,
    title,
    SUM(revenue) AS revenue,
    ROW_NUMBER() OVER (PARTITION BY category ORDER BY SUM(revenue) DESC, title) AS r
  FROM input
  GROUP BY category, title
)
SELECT
  category,
  CASE WHEN r <= 3 THEN title ELSE 'Other' END AS label,
  SUM(revenue) AS revenue
FROM ranked
GROUP BY ALL
ORDER BY category, MIN(r)

Step 6

How much does Other hold?

The same N tells a different story per section. Cookbooks are mostly one book; fiction is mostly tail.

Tableau

WINDOW_SUM(IF RANK_UNIQUE(SUM([revenue]), 'desc') > [Top N] THEN SUM([revenue]) END) / WINDOW_SUM(SUM([revenue]))
// category on Columns, title on Detail; Compute Using: title

SQL

WITH ranked AS (
  SELECT
    category,
    title,
    SUM(revenue) AS revenue,
    ROW_NUMBER() OVER (PARTITION BY category ORDER BY SUM(revenue) DESC, title) AS r
  FROM input
  GROUP BY category, title
)
SELECT
  category,
  SUM(revenue) FILTER (WHERE r > 3)
    / SUM(revenue) AS other_share
FROM ranked
GROUP BY category
ORDER BY other_share DESC

Rows

CategoryRevenue
Fiction87.2%
Children's56.7%
Travel42.7%
Popular science41.5%
Cookbooks34.2%

What happens with ties?

Common mistake

RANK when two titles tie at the cut-off

Two picture books sold exactly the same, tied for third. RANK gives both a 3, so Children's shows four titles at N = 3 while every other section shows three. Use RANK_UNIQUE (ROW_NUMBER in SQL) for exactly N, with a name as the tie-breaker; use RANK when a tie should stay visible.

WrongStarts with Travel, Travel, Travel…
Point at the line to read it.
$0$2K$4KCategories in this order →

Tableau

RANK(SUM([revenue]), 'desc') <= 3

SQL

SELECT category, title, SUM(revenue) AS revenue
FROM input
GROUP BY category, title
QUALIFY RANK() OVER (PARTITION BY category ORDER BY SUM(revenue) DESC) <= 3
RightStarts with Travel, Travel, Travel…
Point at the line to read it.
$0$2K$4KCategories in this order →

Tableau

RANK_UNIQUE(SUM([revenue]), 'desc') <= 3

SQL

SELECT category, title, SUM(revenue) AS revenue
FROM input
GROUP BY category, title
QUALIFY ROW_NUMBER() OVER (PARTITION BY category ORDER BY SUM(revenue) DESC, title) <= 3

Run it on your own data

Any table with a category, an item and a measure works: regions and stores, teams and reps, genres and titles. Drop your CSV and pick the columns; the steps recompute in your browser, and nothing is uploaded.

Drop a CSV anywhere on this page

Up to 10,000 rows. The sample, sales.csv, shows the shape.

Questions

Does a context filter fix Top N per category?

Only when one category is shown at a time. With several categories in the view, the Top N filter still returns N items in total, not N per category.

What does QUALIFY do?

It filters on a window function in the same query, the way HAVING filters on an aggregate: QUALIFY ROW_NUMBER() OVER (PARTITION BY category ORDER BY SUM(revenue) DESC) <= 3. DuckDB, Snowflake and BigQuery support it; elsewhere, wrap the query and filter outside.

Can Tableau collapse the tail into a single Other mark?

Not with a table calculation alone: it labels each title, and each title stays its own mark. Colour by the label and stack the bars, and the tail reads as one block. For a true single Other row, group it in SQL or in your data prep.

Do the hidden titles still count in the category total?

Yes. A table calculation filter runs after totals, so subtotals keep the titles it hides. That's usually what you want: the Other bar and the total both stay honest.

Sources