How to make slow BI dashboards fast: a practical performance playbook
Max Musing
Max Musing Founder and CEO of Basedash · May 19, 2026

Max Musing
Max Musing Founder and CEO of Basedash · May 19, 2026

A BI dashboard that takes thirty seconds to load is not a dashboard. It is a status page that occasionally shows numbers. People stop opening it, stop trusting it, and quietly go back to asking the data team for screenshots in Slack.
This playbook is for analytics engineers, founders, and operators with at least one slow dashboard that nobody has gotten around to fixing. It walks through how to figure out where the time is going, then how to fix it at four layers: the warehouse, the SQL, the BI tool, and the dashboard design itself. Most slow dashboards are slow for one of about ten reasons, and fixing them is usually cheaper than buying a new tool.
Before changing anything, figure out which layer is slow. A dashboard that takes 30 seconds to render can be slow because:
The fix is completely different in each case. Optimizing SQL when the bottleneck is rendering does nothing. Adding a cache when the bottleneck is the warehouse changing partition keys does nothing.
The rest of this guide assumes you have run the dashboard once with the browser dev tools open and the warehouse query log visible, and you know roughly where the seconds are going.
Every BI tool has some version of a query log or performance recorder. Use it before tuning anything.
On the warehouse side, every modern warehouse has a query history view:
SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY (or INFORMATION_SCHEMA.QUERY_HISTORY for the last 7 days).INFORMATION_SCHEMA.JOBS_BY_PROJECT.STL_QUERY / SVL_QUERY_SUMMARY.pg_stat_statements.system.query_log.Pull the slow dashboard’s queries from the log. You usually find one of three patterns:
Write down which pattern applies before doing anything else.
For most internal dashboards, query time is the single biggest contributor. The wins come from a small set of techniques.
The slowest queries usually join two large tables, then filter. Filter first, then join. This is especially important on warehouses where the optimizer is good but not perfect on complex joins, and on PostgreSQL where the planner can mis-estimate.
-- Slow: join, then filter
SELECT u.email, COUNT(o.id)
FROM users u
JOIN orders o ON o.user_id = u.id
WHERE o.created_at >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY u.email;
-- Faster: filter in a CTE, then join
WITH recent_orders AS (
SELECT user_id, id
FROM orders
WHERE created_at >= CURRENT_DATE - INTERVAL '30 days'
)
SELECT u.email, COUNT(o.id)
FROM recent_orders o
JOIN users u ON u.id = o.user_id
GROUP BY u.email;
On Snowflake, BigQuery, and Redshift the difference is usually small because the optimizer pushes the filter down. On PostgreSQL it can be 10x.
If a chart shows daily revenue for the last 90 days, do not pull 90 days of raw transactions and aggregate in the BI tool. Aggregate in SQL and return 90 rows.
This sounds obvious, but BI tools quietly encourage the opposite. A “drag a date field onto a chart” workflow in Looker or Tableau often produces a query that returns far more rows than the chart actually needs. Inspect the generated SQL before you trust the abstraction.
Anything of the form “for each row, look up something in another table” is usually slow. Window functions are almost always faster on a modern warehouse:
-- Slow
SELECT
user_id,
amount,
(SELECT MAX(amount) FROM orders o2 WHERE o2.user_id = o1.user_id) AS user_max
FROM orders o1;
-- Faster
SELECT
user_id,
amount,
MAX(amount) OVER (PARTITION BY user_id) AS user_max
FROM orders;
If a dashboard runs the same aggregation every page load, materialize the result. Three good options, in roughly increasing complexity:
For a fact table with hundreds of millions of rows, a pre-aggregated daily snapshot can take a 25 second query to under a second.
If SQL changes alone do not get the dashboard fast enough, the warehouse may be the bottleneck.
Partition fact tables on the column you filter by, usually a date or event timestamp. A query that filters WHERE event_date >= '2026-01-01' on a partitioned table scans only matching partitions; on an unpartitioned table it scans the whole table.
DATE / TIMESTAMP column; cluster on up to four columns used in WHERE and JOIN clauses.>1 TB); micro-partitioning is automatic but clustering helps prune.ORDER BY clause on the table definition is your partition key; choose it to match dashboard filters.On Snowflake, doubling the warehouse size roughly halves query time, but also roughly doubles cost. For a dashboard hit by 5 people a few times per day, a small warehouse with a short auto-suspend is usually better than a medium that runs all day.
For high-concurrency dashboards (many viewers at once), the issue is often queuing rather than per-query speed. Snowflake’s multi-cluster warehouses, BigQuery’s slot reservations, and Redshift’s concurrency scaling are all ways to handle this.
If your dashboard hits the same PostgreSQL or MySQL database that serves your application, large analytical queries will compete with production traffic. The fix is not a query optimization. The fix is a read replica or a warehouse. We have a longer guide on when to add a data warehouse for teams in this exact spot.
Every BI tool caches. The defaults rarely match the dashboard’s actual usage pattern.
Live queries, scheduled refreshes, and cached extracts each fit different dashboards. A dashboard tracking yesterday’s sales does not need a live query. A dashboard supporting an on-call investigation does. We cover this tradeoff in detail in dashboard refresh strategies.
A practical default: cache aggressively, then add explicit “refresh” buttons for the few dashboards where freshness genuinely matters.
The biggest cache footgun is filters. A dashboard cached for “last 7 days” is a cache miss when a user changes the filter to “last 14 days,” even though the underlying data is identical. Some tools cache per filter combination, some per query, some per dashboard. Read the docs for yours.
If users tend to apply the same handful of filter combinations, pre-warm the cache by scheduling those combinations to refresh during off-hours.
A “cached extract” is a materialized snapshot stored in the BI tool’s own engine: Tableau Hyper, Power BI Import, Looker PDTs, Qlik QVD. Extracts shine when:
They are the wrong choice for sub-hour freshness or for tables that change shape frequently.
Sometimes the dashboard is slow because it is doing too much. The cheapest performance optimization is removing things.
A dashboard with 25 charts is usually running 25 queries. Even if each one takes 0.5 seconds and they run in parallel, you are limited by the slowest one and by the warehouse’s concurrency limits. Cut to the 5-8 charts that actually drive decisions and move the rest to linked detail dashboards.
This is also good design, not just good performance. We have a guide on building dashboards that drive decisions that argues for the same trim.
A bar chart showing revenue by customer for 5,000 customers is unreadable and slow. Top 20 customers plus an “all other” bucket is readable and fast. Apply the same rule to product breakdowns, geography, channel, and any other dimension with a long tail.
A dashboard where every filter triggers a re-query for every chart can produce dozens of queries per interaction. If your BI tool supports it, batch queries or restrict cross-filtering to the charts where it actually matters.
Tables with 50,000 rows are a renderer problem, not a query problem. Either paginate, virtualize, or aggregate before rendering. Some tools handle this automatically; others ship the whole result set to the browser. Check the network tab before assuming.
A few patterns are common to most tools but show up under different names. If you are tuning a specific tool, start here.
| Tool | Most common quick wins |
|---|---|
| Tableau | Switch from Live to Extract; reduce custom SQL; avoid blends in favor of joins; use context filters |
| Power BI | Move from DirectQuery to Import where possible; reduce DAX measures with iterators (SUMX, FILTER); use aggregations; star schema, not flat |
| Looker | Use PDTs for expensive joins; merge results sparingly; check derived_tables for unintended re-runs |
| Metabase | Cache questions; turn on query caching with a sane TTL; use models for shared logic; avoid X-rays on large fact tables |
| Sigma | Use materializations; restrict ad-hoc workbook expansion on large tables |
| Mode / Hex | Run heavy queries in scheduled jobs; cache results; reduce notebook cell count on shared reports |
| Basedash | Use AI-generated views for repeated queries; rely on built-in query caching; connect to a read replica when querying production data |
| Cube / dbt Semantic Layer | Pre-aggregate roll-ups for common dashboard granularities; alias to materialized tables for repeated cuts |
The general pattern across all of them: move work earlier in the pipeline (warehouse, extract, model) and ask the BI tool to do less at query time.
If you have a slow dashboard and 30 minutes, run this:
DISTINCT, or aggregates on raw rows. Try a CTE-first rewrite.Most slow dashboards drop from “annoyingly slow” to “fine” with one or two of these moves. If they do not, the underlying problem is usually that you are trying to do real-time analytics on the wrong stack, and a deeper rethink is in order.
A small number of dashboards are slow because the architecture is wrong for the job. Signs:
Treat performance work as a way to find these mismatches early. A dashboard that cannot be made fast within the current stack is telling you something about the stack.
Use this when you ship a new dashboard or audit an existing one. None of these are absolute rules, but each one is worth a deliberate decision.
A dashboard that passes this checklist loads in a couple of seconds on most stacks. One that fails three or more of them is almost certainly going to feel slow no matter which BI tool it lives in.
The right mental model is that BI dashboard performance is a property of the whole pipeline, not the BI tool. Tools like Basedash, Metabase, Looker, Power BI, and Tableau all surface the same underlying queries against the same warehouse. The fast ones are the ones where the team has done the work upstream: clean models, sensible aggregations, the right caching strategy, and dashboards that ask reasonable questions. Most slow dashboards become fast dashboards once that work is done.
Written by
Founder and CEO of Basedash
Max Musing is the founder and CEO of Basedash, an AI-native business intelligence platform designed to help teams explore analytics and build dashboards without writing SQL. His work focuses on applying large language models to structured data systems, improving query reliability, and building governed analytics workflows for production environments.
Basedash lets you build charts, dashboards, and reports in seconds using all your data.