content
Finding stale content in Tableau Server — and retiring it without a fight
Karan Arora · Founder · 6 min read · Last reviewed:
Tableau estates grow in one direction. Publishing a workbook takes a minute and feels like progress; deleting one takes a meeting and feels like risk. So nobody deletes, and every Server accumulates a sediment layer of dashboards whose last viewer changed jobs two reorgs ago — still stored, still refreshing, still holding a backgrounder slot every morning.
The case for cleaning it up is not tidiness. Stale content carries two measurable costs — storage and refresh capacity — and the repository can put a number on both. This guide covers where Tableau records usage, the SQL that produces a defensible stale list, the trap that seasonal content sets, and a retirement process that does not upset anyone.
Where usage is recorded
The repository tracks view access in views_stats: per the data
dictionary, one row per user per view, with nviews keeping "a
cumulative count of the number of times the view was accessed by this
user" and time holding "the time of the most recent access of the
specified view by the specified user." Roll that up to the workbook and
you have last-touch for everything on the server.
Two properties of this table shape everything downstream. It is
cumulative, not event-level — it can tell you when a workbook was last
opened and how many times ever, but not the shape of its usage over
time. And like every repository table, it only reaches back as far as it
reaches back; the event-level alternative, historical_events, is
bounded by your retention settings —
querying the repository safely
covers both the access setup and the MIN(created_at) check that tells
you your real window.
The stale list
The query is a left join, because the workbooks that matter most are the ones with no usage row at all — published and never opened. Group by workbook id, not name: identically-named workbooks in different projects are different workbooks, and merging them hides exactly the duplication a cleanup should surface.
SET TRANSACTION READ ONLY;
SELECT
w.name AS workbook,
MAX(vs.time) AS last_accessed,
COALESCE(SUM(vs.nviews), 0) AS lifetime_views,
ROUND(w.size / 1048576.0) AS size_mb
FROM workbooks w
LEFT JOIN views v ON v.workbook_id = w.id
LEFT JOIN views_stats vs ON vs.view_id = v.id
GROUP BY w.id, w.name, w.size
HAVING MAX(vs.time) IS NULL
OR MAX(vs.time) < NOW() - INTERVAL '90 days'
ORDER BY w.size DESC;The 90-day threshold is ours, not Tableau's — it is the convention our rule library uses and the one this site quotes ("GB not accessed in 90+ days"). Tableau's own Stale Content admin view lets you set the threshold anywhere from 1 to 120 days, which is the documented way of saying the same thing: the right number is a policy decision, not a constant. Pick one, write it down, and apply it evenly.
w.size is documented as "the number of bytes used in storing the
workbook information" — summing it over the stale list prices the
storage half of the problem in one line.
The expensive half: refreshes nobody reads
Storage is the visible cost and rarely the important one. The offenders that matter are scheduled extracts on unwatched workbooks, because they spend the scarcer resource: backgrounder time. Join the stale list against the job history:
SELECT
bj.title AS workbook,
COUNT(*) AS refreshes_90d,
ROUND(SUM(EXTRACT(EPOCH FROM
bj.completed_at - bj.started_at)) / 3600, 1) AS backgrounder_hours
FROM background_jobs bj
WHERE bj.job_name IN ('Refresh Extracts', 'Increment Extracts')
AND bj.created_at >= NOW() - INTERVAL '90 days'
AND bj.title IN (/* names from the stale list */)
GROUP BY bj.title
ORDER BY backgrounder_hours DESC;One caveat, in a guide about measuring carefully: background_jobs.title
holds the workbook's name, not its id, so this join is a name match —
exact on most estates, ambiguous wherever two projects hold
identically-named workbooks. Check yours before trusting the split:
SELECT name FROM workbooks GROUP BY name HAVING COUNT(*) > 1; — any
rows it returns are titles whose refresh numbers arrive merged across
the duplicates.
Every hour in that result is an hour some watched workbook queued behind a refresh nobody will open — the same queue arithmetic as diagnosing backgrounder delay, spent on content with no reader. In the estates we review, this list is where the retirement conversation stops being philosophical: "four hours of nightly backgrounder time serving zero viewers" is a sentence budget owners act on. Stale content is also one of the eight areas the estate-wide audit we run measures, priced against everything else competing for the same capacity.
The platform already agrees with you
Tableau ships two features that concede the point. The Stale Content admin view shows content unused within your chosen threshold alongside the disk space it holds, and since 2020.3 lets you select and tag content as stale — up to 1,000 objects at a time — then filter for the tag in Explore and archive or delete in bulk. With Advanced Management, the documented pattern goes further: the Content Migration Tool can pick up tagged content on a schedule and move it to an archive project automatically.
And Tableau can suspend refreshes for inactive workbooks on its own: a site setting suspends extract refresh tasks after a configurable 7 to 100 days of inactivity — 32 by default. The documented scope matters: full refreshes only, and only tasks running more frequently than weekly, so a weekly incremental refresh on a dead workbook keeps running forever. Auto-suspension trims the symptom; it does not shrink the estate. Suspended-but-stored content still counts in the storage query above — and as covered in why extract refreshes fail, suspended tasks vanish from failure counts, which makes them easy to mistake for fixed.
The seasonal trap
A 90-day threshold has one systematic false positive: content used on a cycle longer than the threshold. The quarter-end close pack looks dead at day 89 and indispensable at day 91; the annual planning dashboard looks dead for eleven months. Delete either on the strength of one query and you will meet its owner at the worst possible moment.
views_stats cannot resolve this by itself — cumulative counts have no
shape. The event-level check is historical_events, windowed by month:
a workbook with access spikes in January, April, July, and October is
quarterly, not dead. Two practical rules from our reviews, both ours
rather than Tableau's: judge nothing against a window shorter than
thirteen months of history where retention allows it, and treat "zero
lifetime views" differently from "no recent views" — the first was never
adopted and is safe to retire aggressively; the second earned its keep
once and deserves the owner conversation.
Retiring without a fight
The process that works is boring and staged, and none of it is a Tableau feature — it is sequencing.
Notify first. Mail the owner list from the stale query with the numbers attached — last access, lifetime views, refresh hours. Give a response window and make silence consent. Most of the list gets no reply, which is itself the answer.
Archive, don't delete. Move unclaimed content to an archive project with permissions locked to administrators — the admin view's stale tag plus the Explore filter does this in bulk. The content disappears from every user's view of the server, which is the real test: whoever notices was a user you did not know about, and restoring from a project move is one action.
Delete on a stated schedule. After a published grace period — we suggest one more quarter, so the seasonal trap gets a final chance to spring — delete the archive project's contents. Deletion is now a non-event, because everything in it has already been invisible for months.
Run the stale query again the following quarter and the estate has a pulse: what was added, what went quiet, what the archive absorbed. That cadence, not any single cleanup, is what keeps a Server lean.
Tableau Cloud: same question, different instruments
Cloud has no views_stats to query. The equivalents live in the Admin
Insights project — the Site Content data source carries the content
inventory and the TS Events data source records access events — and the
same staged retirement applies unchanged. The full Cloud toolkit is
its own guide.
Deletion is a feature
An estate where everything is kept is an estate where nothing can be found, and where every real dashboard queues behind a ghost. The repository gives you the list, the platform gives you the tools, and the staged process gives you cover. The only thing Tableau cannot supply is the decision — and the numbers make that easier than it looks.