DEV Community

Cover image for Tableau vs Power BI vs Looker vs Sigma: BI Tools for Data Engineers in 2026
Gowtham Potureddi
Gowtham Potureddi

Posted on

Tableau vs Power BI vs Looker vs Sigma: BI Tools for Data Engineers in 2026

tableau vs power bi used to be the whole BI-tool interview question — and in 2026 it is only the opening move. The reality on the ground is a four-cornered choice between Tableau (visualization-first, VizQL + Hyper), Power BI (DAX + Vertipaq + Fabric OneLake), Looker (LookML semantic layer with git-based governance), and Sigma (spreadsheet-first pushdown BI over your cloud warehouse). Each corner defines a different answer to three deceptively simple questions: who authors the metric, where does the semantic layer live, and what is the cost model per active user?

This guide is the senior-DE comparison you wished existed the first time an interviewer asked "why did your team pick power bi vs tableau?" or "how would you migrate off looker vs tableau in a Snowflake shop?" It walks through the Tableau execution model (drag-drop → VizQL → SQL → Hyper), the Power BI stack (DAX language, Vertipaq columnar engine, Import vs Direct Query, Fabric OneLake integration), the Looker semantic-layer model (LookML views + models + explores, git-based dev flow, persistent derived tables), and the Sigma pushdown workbook (spreadsheet UX compiled to SQL against Snowflake / BigQuery / Databricks, input-table writeback). Each section pairs a teaching block with a Solution-Tail interview answer — code, a step-by-step trace, an output table, then a concept-by-concept breakdown of why it works.

PipeCode blog header for Tableau vs Power BI vs Looker vs Sigma — bold white headline 'Tableau · Power BI · Looker · Sigma' over a hero composition of four small glyph medallions (chart-mark, funnel-cube, gear-scroll, spreadsheet-grid) arranged on a wheel around a central purple 'pick one' seal, on a dark gradient.

When you want hands-on reps on the SQL that every BI tool eventually compiles down to, drill the SQL practice library →, rehearse the aggregation problems → that dashboards live and die by, and sharpen the window-functions library → for the running totals and rank patterns every BI tool asks about.


On this page


1. Why the BI-tool choice matters in 2026

The BI tool is a data-platform bet — it defines authorship, semantic layer, cost model, and the whole migration surface

The one-sentence invariant: a BI tool is a foundational data-platform bet, not a visualization purchase — it decides who owns each metric, where the semantic layer lives, whether you push down or extract, and what a user costs per month. Every other comparison — chart library, custom viz support, refresh cadence, mobile app — follows from those four axes. Once you internalise "BI is authorship + semantic layer + execution + cost," the entire tableau vs power bi interview surface collapses to a sequence of consequences from those four choices.

The four axes interviewers actually probe.

  • Authorship. Who writes a new measure — an analyst in a drag-drop canvas (Tableau, Sigma), a data engineer in DAX (Power BI), or a modeler in a LookML file that goes through pull request (Looker)? The choice determines who is on-call for a broken KPI.
  • Semantic layer. Where do "MRR", "active user", "churn" live? Inside the report (Tableau calc field), inside a shared model (Power BI dataset, Looker LookML, Sigma dataset), or outside the tool entirely in dbt / Cube / MetricFlow? Getting this wrong causes the classic "three dashboards, three different revenue numbers" bug that every senior DE has fixed at least once.
  • Execution model. Does the tool extract data into its own engine (Tableau Hyper, Power BI Import + Vertipaq) or push down every query as SQL to the warehouse (Sigma, Looker, Power BI Direct Query, Tableau Live)? Pushdown pays the warehouse per query; extract pays memory + refresh scheduling.
  • Cost model. Per-Creator + per-Explorer + per-Viewer seat (Tableau), per-user Pro + Premium capacity (Power BI), platform + per-editor + per-viewer (Looker), per-user + warehouse compute (Sigma). The pricing pattern determines whether a 5,000-employee rollout is a $500K/year line item or a $2M/year one.

Who authors the metric — the deepest split.

  • Analyst-authored (Tableau, Sigma). A business analyst opens the tool, drags a field, writes a calculated field or spreadsheet formula. Fast iteration, huge iteration surface — and the same measure defined ten different ways across ten workbooks.
  • Engineer-authored (Looker). A modeler edits .lkml files, commits to git, opens a pull request, gets it reviewed. Slow but governed — every measure has an owner, a history, and a review trail.
  • Hybrid (Power BI). Analysts can build in Power BI Desktop; engineers can promote datasets to a Premium workspace and turn them into a shared semantic model. Both authorship modes live in the same tool.

The 2026 reality — what changed since 2022.

  • Fabric ate Power BI Premium. Microsoft merged Power BI Premium capacities into Microsoft Fabric (2023–2024), so buying Power BI at scale now means buying Fabric. OneLake is the underlying storage; Power BI is a workload on top.
  • Looker is now two products. Google runs Looker (classic) for enterprise LookML + embed, and Looker Studio Pro (formerly Data Studio) as the free-tier / SMB product. They share brand, not code.
  • Tableau added Pulse + Tableau AI. Salesforce shipped Tableau Pulse (mobile-first AI insights) in 2023 and Tableau AI (natural language + generative assist) in 2024–2025. Ask Data was retired.
  • Sigma took real market share in Snowflake shops. Spreadsheet-first pushdown BI grew from niche to serious contender in 2024–2026, especially with input-table writeback becoming a first-class feature.
  • dbt Semantic Layer + MetricFlow. More teams push the semantic layer out of BI entirely into dbt / MetricFlow / Cube, then let any BI tool consume the shared metric definitions. This is now a real architecture, not a whiteboard idea.

What interviewers listen for.

  • Do you say "BI tool = authorship + semantic layer + execution + cost" in the first sentence? — senior signal.
  • Do you distinguish "in-tool semantic layer vs dbt / MetricFlow" unprompted? — senior signal.
  • Do you mention "per-viewer vs per-capacity" cost model when comparing tools? — required answer.
  • Do you push back on "tool X is better than tool Y" with "better for what team, what stack, what budget"? — senior signal.

Worked example — same MRR metric in four tools

Detailed explanation. The classic BI hello-world — compute monthly recurring revenue (MRR) as SUM(subscription.mrr) grouped by month — looks trivially similar in all four tools. The runtime shape and where the definition lives are wildly different. Tableau puts it in a workbook calc field; Power BI puts it in a DAX measure; Looker puts it in view.lkml; Sigma puts it in a spreadsheet cell formula.

Question. Author the "MRR by month" metric in Tableau, Power BI (DAX), Looker (LookML), and Sigma (spreadsheet). Show where the definition lives, who owns it, and what SQL each tool eventually emits.

Input. subscriptions table in the warehouse.

subscription_id customer_id mrr start_date
s1 c1 100 2026-05-15
s2 c2 250 2026-06-01
s3 c1 50 2026-06-20
s4 c3 400 2026-07-01

Code.

# Tableau — calculated field inside the workbook (workbook-scoped)
Field name: MRR
Formula:    SUM([mrr])
Grouping:   drag [start_date] to Columns, set to MONTH(start_date)
Enter fullscreen mode Exit fullscreen mode
// Power BI — DAX measure in a shared semantic model
MRR = CALCULATE(SUM(subscriptions[mrr]))

// Used in a matrix visual with Rows = FORMAT(subscriptions[start_date], "yyyy-MM")
Enter fullscreen mode Exit fullscreen mode
# Looker — LookML measure in views/subscriptions.view.lkml (git-tracked)
view: subscriptions {
  sql_table_name: warehouse.subscriptions ;;
  dimension: start_month {
    type: date_month
    sql: ${TABLE}.start_date ;;
  }
  measure: mrr {
    type: sum
    sql: ${TABLE}.mrr ;;
    value_format_name: usd
  }
}
Enter fullscreen mode Exit fullscreen mode
# Sigma — spreadsheet cell in a workbook (analyst-scoped)
Column: Month     = TruncDate("month", [Start Date])
Column: MRR       = Sum([Mrr])
Group by: Month
Sort:      Month asc
Enter fullscreen mode Exit fullscreen mode

Step-by-step explanation.

  1. Tableau stores the calc field inside the workbook (.twb / .twbx) — if another workbook wants MRR, an analyst copies the formula. Two workbooks can define MRR differently and never notice.
  2. Power BI stores the DAX measure in the shared semantic model (dataset). Every report that binds to the model uses the same measure. Break the measure once, break every report — but at least the definition is single-sourced.
  3. Looker stores the measure in .lkml under version control. A change to measure: mrr requires a pull request; a merge deploys to every explore + dashboard that uses it. The metric is fully governed.
  4. Sigma stores the formula in a workbook cell (like Excel). By default it is workbook-scoped; if promoted into a shared Dataset, other workbooks inherit the definition. Governance is opt-in.
  5. All four tools eventually emit the same SQL — SELECT DATE_TRUNC('month', start_date) AS month, SUM(mrr) AS mrr FROM subscriptions GROUP BY 1 — but only Looker forces the definition to live in one place from day one.

Output (MRR by month, all four tools compute the same numbers).

month mrr
2026-05 100
2026-06 300
2026-07 400

Rule of thumb. If the team already ships bugs like "the MRR chart on Slide 12 doesn't match the finance dashboard," the semantic layer is the fix — not the BI tool. Pick Looker (or push MRR into dbt / MetricFlow) before switching visualization vendors.

Worked example — extract vs pushdown on a 500M-row fact table

Detailed explanation. A common interview question — "you have a 500M-row events table in Snowflake and 200 analysts. How does each BI tool answer a dashboard query, and what does the warehouse bill look like?" The answer is the cleanest demonstration of the extract-vs-pushdown split.

Question. Given a 500M-row Snowflake table and a "top-10 events by day" dashboard viewed 5,000 times a day, describe the execution path in Tableau (Extract), Tableau (Live), Power BI (Import), Power BI (Direct Query), Looker, and Sigma. Show the warehouse cost implication.

Input.

Tool + mode Data location Query path
Tableau Extract Hyper file on Tableau Server dashboard → Hyper
Tableau Live Snowflake dashboard → SQL → Snowflake
Power BI Import Vertipaq on Fabric capacity dashboard → Vertipaq
Power BI Direct Query Snowflake dashboard → DAX → SQL → Snowflake
Looker Snowflake (with PDT optional) dashboard → SQL → Snowflake
Sigma Snowflake dashboard → SQL → Snowflake

Code.

-- The SQL that Tableau Live / Power BI Direct Query / Looker / Sigma
-- all end up sending to Snowflake for the "top-10 events by day" dashboard.
SELECT
    DATE_TRUNC('day', event_time) AS d,
    event_name,
    COUNT(*)                      AS n
FROM   events
WHERE  event_time >= CURRENT_DATE - 30
GROUP BY 1, 2
ORDER BY d DESC, n DESC
LIMIT 10;
Enter fullscreen mode Exit fullscreen mode
# Tableau Extract refresh schedule (extract lives on Server, refresh nightly)
extract:
  source: snowflake.analytics.events
  filter: event_time >= dateadd(day, -30, current_date)
  schedule: cron(0 3 * * *)   # 03:00 UTC every day
  compression: high
  incremental: true
  incremental_key: event_id
Enter fullscreen mode Exit fullscreen mode

Step-by-step explanation.

  1. Tableau Extract materialises the top-10 aggregate (or the raw filtered set) into a Hyper file on Tableau Server nightly. 5,000 daily views hit Hyper, not Snowflake. Warehouse cost: 1 query/day. Latency: data is up to 24 h stale.
  2. Tableau Live compiles every dashboard load into SQL and sends it to Snowflake. 5,000 views = 5,000 queries. Snowflake result cache reduces the actual compute cost, but warehouse credit is still charged for each hit past the cache TTL.
  3. Power BI Import loads a compressed columnar copy of the filtered slice into Vertipaq on the Fabric capacity. Same math as Tableau Extract: 1 warehouse query per refresh, 5,000 in-memory queries per day.
  4. Power BI Direct Query sends every DAX-generated SQL query to Snowflake. Same cost shape as Tableau Live. Composite models mix Import + Direct Query on one dataset to trade freshness for cost.
  5. Looker by default pushes down every query. For expensive aggregates, LookML derived_table with persist_for materialises to a persistent derived table (PDT) in the warehouse — Snowflake stores the aggregate, dashboards query the PDT instead of the raw events.
  6. Sigma always pushes down. Every workbook load = SQL to Snowflake. No extract, no data movement. Snowflake result cache is the only cost mitigation.

Output (cost profile for 5,000 daily dashboard hits).

Tool + mode Warehouse queries/day Refresh latency Governance
Tableau Extract ~1 up to 24 h workbook
Tableau Live ~5,000 (cache reduces) seconds workbook
Power BI Import ~1 up to 24 h dataset
Power BI Direct Query ~5,000 (cache reduces) seconds dataset
Looker (LookML) ~5,000 (PDT cuts fact scans) seconds git
Sigma ~5,000 (cache reduces) seconds workbook / dataset

Rule of thumb. For very high viewership dashboards on cost-sensitive warehouses, prefer extract-based tools (Tableau Extract, Power BI Import). For fresh-data-mandatory dashboards, prefer pushdown tools (Sigma, Looker, Direct Query) and rely on materialised aggregates in the warehouse.

Worked example — a metric-drift bug reveals the semantic-layer choice

Detailed explanation. A classic senior-DE question is "your finance team complains the CFO dashboard shows $1.2M in revenue this week but the sales dashboard shows $1.4M. How would you fix this, and how would your fix differ across the four BI tools?" The answer isolates the semantic-layer axis.

Question. Two dashboards show different revenue numbers. Walk through how you would trace and fix the discrepancy in Tableau, Power BI, Looker, and Sigma. Which tools prevent the bug and which merely help you find it?

Input. Two dashboards, both claim to show "Revenue this week".

Dashboard Definition
CFO board SUM(invoices.amount) WHERE status='paid' AND invoice_date >= week_start
Sales board SUM(orders.subtotal) WHERE order_date >= week_start

Code.

-- The two hidden queries — subtly different logic.
-- CFO board:
SELECT SUM(amount) FROM invoices
WHERE status = 'paid' AND invoice_date >= date_trunc('week', current_date);

-- Sales board:
SELECT SUM(subtotal) FROM orders
WHERE order_date >= date_trunc('week', current_date);
-- ^^ note: subtotal excludes tax + shipping; invoices.amount includes them
Enter fullscreen mode Exit fullscreen mode
# Looker fix — single source of truth in views/revenue.view.lkml
view: revenue {
  derived_table: {
    sql:
      SELECT
        DATE_TRUNC('week', invoice_date) AS week,
        SUM(amount)                      AS gross_revenue,
        SUM(CASE WHEN status = 'paid' THEN amount END) AS paid_revenue
      FROM warehouse.invoices
      GROUP BY 1
    ;;
  }
  measure: revenue {
    type: sum
    sql: ${TABLE}.paid_revenue ;;
    description: "Weekly revenue, paid invoices only. See PR #1204."
  }
}
Enter fullscreen mode Exit fullscreen mode

Step-by-step explanation.

  1. Tableau. Both dashboards' calc fields live inside their own workbooks. To fix, an analyst has to open both .twbx files, diff the calc fields, agree on one definition, and manually update both. Nothing in the tool prevents the drift from re-appearing next week.
  2. Power BI. If both dashboards use the same shared dataset with a Revenue measure, they agree by construction. If one dashboard uses a personal Revenue measure defined in the report, the drift is possible. Fix: promote all measures to the shared dataset; disable report-scoped measures.
  3. Looker. Both dashboards call revenue.revenue from the shared LookML measure. To change the definition, you edit .lkml, open a PR, get it reviewed, merge — every dashboard updates atomically. The drift is architecturally impossible.
  4. Sigma. By default each workbook has its own formulas. Fix: promote Revenue into a shared Sigma Dataset; make workbooks reference the dataset column, not their own formula.
  5. The pattern: Tableau + Sigma default to workbook-scoped definitions (analyst-friendly, drift-prone). Power BI splits by whether you enforce shared datasets. Looker enforces a shared model from day one.

Output (semantic-layer strength ranking for this bug).

Tool Prevents drift by default? Fix effort once drift appears
Tableau no high (edit both workbooks)
Power BI only with shared dataset medium
Looker yes (LookML + git) low (one PR fixes all)
Sigma only with shared dataset medium

Rule of thumb. If metric drift is a real, ongoing pain (three dashboards, three revenue numbers), pick a tool with a first-class semantic layer or push the semantic layer out to dbt / MetricFlow. The BI tool is not the fix by itself; the fix is "one metric definition + strong reference discipline".

Senior interview question on BI-tool selection

A senior interviewer often opens with: "Walk me through how you would choose between Tableau, Power BI, Looker, and Sigma for a new data platform in 2026. What are the four or five questions you ask, in order, and what answer to each one pushes you to one tool over the others?"

Solution Using a 5-question BI decision framework

Decision framework — Tableau vs Power BI vs Looker vs Sigma

1. Which cloud stack are you on?
   - Microsoft (Azure + Fabric)                        → Power BI
   - Google (BigQuery)                                 → Looker native
   - Snowflake / Databricks (independent)              → Tableau / Looker / Sigma
   - AWS + Salesforce (Salesforce customer)            → Tableau native

2. Where does the semantic layer live?
   - In dbt / MetricFlow / Cube                        → any tool with dbt Semantic
   - In the BI tool                                    → Looker (LookML) or Power BI (dataset)
   - Nowhere; per-workbook calc fields                 → Tableau / Sigma

3. Who authors the metric?
   - Analyst in drag-drop / spreadsheet                → Tableau / Sigma
   - Data engineer in DAX                              → Power BI
   - Modeler in code review (git)                      → Looker

4. Embedded analytics — customer-facing dashboards?
   - Yes, mission critical                             → Looker (embed SDK is class-leading)
   - Yes, side feature                                 → Power BI Embedded or Tableau Embedded
   - No, internal only                                 → Sigma / Tableau / Power BI

5. Total cost of ownership (TCO) shape?
   - Per-viewer scales with employee count             → Tableau Viewer / Sigma Viewer
   - Per-capacity flat rate                            → Power BI Premium / Fabric F-SKU
   - Per-editor + free-viewer                          → Looker
Enter fullscreen mode Exit fullscreen mode

Step-by-step trace.

Team Q1 stack Q2 semantic Q3 author Q4 embed Q5 TCO Picked
Snowflake B2B SaaS with dbt Snowflake dbt analyst embed critical per-editor Looker
Microsoft F500 finance Azure dataset engineer none capacity Power BI
Snowflake data-app startup Snowflake in-tool analyst none per-viewer Sigma
Salesforce CRM shop AWS + SFDC in-tool analyst none per-viewer Tableau

After the 5-question pass, the tool choice is usually unambiguous. Where two tools tie, the tiebreak is whichever the team already knows.

Output:

Tool When it wins
Tableau Visualization-first culture, Salesforce integration, extract-heavy analytics, huge analyst community
Power BI Microsoft / Fabric stack, DAX expertise, per-capacity pricing, tight Office 365 integration
Looker dbt or LookML semantic layer, embedded analytics, git-based governance, engineer-authored metrics
Sigma Snowflake / BigQuery / Databricks pushdown, spreadsheet-first analysts, input-table writeback needs

Why this works — concept by concept:

  • Cloud stack — the structural gate — Microsoft shops overwhelmingly pick Power BI because it is included in E5 and integrated with Fabric; Google Cloud shops lean Looker; independent stacks are open to all four. Q1 eliminates one or two options before you ever compare features.
  • Semantic layer — the drift-prevention axis — if metric drift is a real pain, the fix is a governed semantic layer (Looker LookML, Power BI shared dataset, or dbt Semantic). Tools without one push the problem back on humans.
  • Authorship — who owns the metric — analyst-authored tools iterate fast but rot fast; engineer-authored tools ship slowly but stay clean. Match the tool to the team you have, not the team you wish you had.
  • Embedded analytics — Looker's embed SDK is the industry standard for customer-facing dashboards. Power BI Embedded is a real option; Tableau Embedded exists; Sigma is emerging. If embed is core, Looker is often the pick.
  • Cost — Tableau + Sigma scale linearly with viewer count; Power BI Premium / Fabric is flat capacity; Looker mixes platform + per-editor. For 10K+ viewers, per-capacity often wins; for 500 focused analysts, per-viewer stays cheaper.

SQL
Topic — SQL
BI-tool foundational SQL problems

Practice →

SQL Topic — aggregation Dashboard-style aggregation problems

Practice →


2. Tableau — visualization-first, VizQL + Hyper

tableau vs power bi — Tableau is the visualization-first BI tool where VizQL compiles drag-drop marks into SQL and Hyper answers extracts

The mental model in one line: a Tableau workbook is a set of drag-drop worksheets whose "marks" the VizQL engine translates into SQL (or MDX for OLAP sources), then either Hyper (the in-memory columnar engine) or the live source answers the query. Once you say "marks compile to VizQL compile to SQL," every power bi vs tableau interview question becomes a deduction from "how does Tableau turn a drag into an executable query?"

Iconographic Tableau engine diagram — a drag-drop worksheet card on the left with three pill-shaped shelves, a purple VizQL translator wheel in the middle turning marks into a SQL scroll, and a green Hyper in-memory columnar cabinet on the right receiving the query, with an extract vs live toggle chip at the bottom.

The three core abstractions.

  • Marks + shelves. The user drags dimensions to Columns / Rows / Filters shelves and measures to the Marks card. Each combination of shelves + marks defines an implicit query.
  • VizQL. Tableau's proprietary language that takes the mark specification and compiles it into a query against the underlying data source (SQL for relational, MDX for cubes, custom dialects for Salesforce / Google Analytics).
  • Hyper. The in-memory columnar engine (replaced the older TDE format in 2018). Stores extract files (.hyper) and answers queries at RAM-speed. Also usable as a query engine on external data via Hyper API.

Extract vs Live connection — the execution split.

  • Extract (.hyper). Tableau materialises a snapshot of the data source into a Hyper file, either on Desktop or Tableau Server / Cloud. Dashboards query the extract. Refresh is scheduled (or on-demand). Fast, offline, stale.
  • Live connection. Every dashboard load compiles VizQL into SQL and sends it to the source. Fresh, always-current, warehouse-billable per query. The two modes can be swapped on a workbook without changing the workbook logic.

Tableau Prep — the ETL companion.

  • Drag-drop ETL tool for pre-shaping data before it lands in a workbook. Nodes: input, clean, aggregate, join, union, script, output.
  • Prep Conductor (part of Data Management add-on) schedules Prep flows on Tableau Server / Cloud.
  • Not a serious contender against dbt / Airflow / Prefect for engineered pipelines, but a real productivity win for analyst-owned data prep.

Tableau Server vs Tableau Cloud — the governance layer.

  • Tableau Server — self-hosted, on your VMs or Kubernetes. You own everything: SSO, backups, upgrades, capacity.
  • Tableau Cloud — Salesforce-managed SaaS. Same product, no ops. Increasingly the default for new customers post-2023.
  • Governance features: projects, workbook permissions, data source permissions, subscribers, row-level security via user filters, custom views.

Salesforce integration and the AI layer.

  • Salesforce acquired Tableau in 2019. Post-2023, Tableau leans into Salesforce Data Cloud as a native source and into Salesforce-first customers.
  • Tableau Pulse (2023) — mobile-first AI-generated insight cards per metric. Subscribes users to metric changes.
  • Tableau AI (2024–2025) — natural language question-asking (successor to Ask Data), assisted calc field generation, description-generation for metrics.
  • Einstein GPT for Tableau — the Salesforce GenAI layer, useful for Salesforce-native customers, less so for independent Snowflake shops.

Pricing model — the three-tier seat.

  • Creator — full authoring (Desktop + Server access). Around $75 / user / month at list.
  • Explorer — web authoring on published data sources. Around $42 / user / month.
  • Viewer — read-only interaction with published dashboards. Around $15 / user / month.
  • All prices vary by contract; per-viewer scales linearly with employee count — a rollout of 10,000 viewers is a real six- or seven-figure line item.

Common interview probes on Tableau.

  • "What is the difference between Extract and Live connections?" — Extract is a Hyper snapshot; Live is compile-to-SQL per query.
  • "How does VizQL work?" — translates the marks/shelves specification into a query optimised for the connected source.
  • "When would you use Tableau Prep instead of dbt?" — for analyst-owned quick shaping; not for engineered production pipelines.
  • "How is Tableau priced?" — three-tier seat (Creator / Explorer / Viewer), linear scaling with active users.

Worked example — bar chart marks compiled to SQL

Detailed explanation. An analyst drags region to Columns, SUM(revenue) to Rows, and sets Marks type to Bar. Tableau's VizQL turns this into a straightforward SELECT region, SUM(revenue) FROM sales GROUP BY region ORDER BY region against the connected source. Understanding this translation is the entire mental model for Tableau performance work.

Question. Given a Snowflake sales table connected Live, show the SQL that Tableau emits for a bar chart of SUM(revenue) by region. Then show the same for SUM(revenue) by region, product_category as a stacked bar.

Input. sales table.

sale_id region product_category revenue
1 NA electronics 500
2 NA apparel 200
3 EU electronics 300
4 EU apparel 400
5 APAC electronics 700

Code.

-- Bar chart: [region] on Columns, [SUM(revenue)] on Rows, Marks = Bar
SELECT
    region,
    SUM(revenue) AS "sum:revenue:ok"
FROM   sales
GROUP BY 1
ORDER BY 1;

-- Stacked bar: [region] on Columns, [SUM(revenue)] on Rows,
-- [product_category] on Colour of the Marks card
SELECT
    region,
    product_category,
    SUM(revenue) AS "sum:revenue:ok"
FROM   sales
GROUP BY 1, 2
ORDER BY 1, 2;
Enter fullscreen mode Exit fullscreen mode

Step-by-step explanation.

  1. VizQL reads the workbook's specification: Columns=[region], Rows=[SUM(revenue)], Marks=Bar. It infers GROUP BY region because region is a dimension on a shelf and SUM(revenue) is a measure.
  2. It emits SQL against the source connection (Snowflake in this case). The "sum:revenue:ok" alias is Tableau's internal naming convention for aggregate outputs.
  3. Adding product_category to the Colour encoding on the Marks card introduces a second grouping key. VizQL adds it to SELECT and GROUP BY.
  4. On Live mode, this SQL is sent to Snowflake every time the dashboard loads. Snowflake result cache means repeat loads within TTL don't re-execute — but only if the SQL is byte-identical.
  5. On Extract mode, the same SQL is sent to Hyper (which stores the extracted data) — no warehouse cost per load; only per extract refresh.

Output (bar chart data).

region sum_revenue
APAC 700
EU 700
NA 700

Rule of thumb. For any Tableau performance investigation, capture the workbook's Performance Recording, read the emitted SQL from Tableau Log Viewer, and treat that SQL as any other query in your database. All Tableau tuning reduces to "make the SQL fast" — indexes, aggregates, materialised views, warehouse warehouse size.

Worked example — extract refresh with incremental strategy

Detailed explanation. A workbook is backed by a 100M-row events table. Full refresh every hour is too slow. Tableau supports incremental refresh — append new rows since the last extract by a monotonic key. Configuring this correctly is a common interview scenario.

Question. Configure a Tableau extract on events(event_id BIGINT, event_time TIMESTAMP, ...) with incremental refresh keyed on event_id. Show the refresh command and explain what happens on schedule.

Input. events table (append-only, event_id is auto-increment).

event_id event_time user_id
1 2026-07-01 08:00 u1
2 2026-07-01 08:05 u2
... ... ...
100000000 2026-07-17 09:00 uN

Code.

# In Tableau Desktop → Data Source → Extract (Edit)
extract:
  connection: snowflake.analytics.events
  filters:
    - event_time >= dateadd(day, -30, current_date)
  aggregate: false
  incremental:
    enabled: true
    key_column: event_id     # monotonic; must be non-decreasing
  storage: hyper
  refresh_schedule: hourly
Enter fullscreen mode Exit fullscreen mode
# Trigger a refresh via Tableau Server REST API (from a scheduler / CI)
curl -X POST "https://tableau-cloud/api/3.20/sites/<siteId>/datasources/<dsId>/refresh?type=incremental" \
     -H "X-Tableau-Auth: <token>"
Enter fullscreen mode Exit fullscreen mode

Step-by-step explanation.

  1. On the first refresh, Tableau extracts every row that matches the filter (event_time >= today - 30) into a Hyper file on Server. It records the MAX(event_id) from the extracted rows as the incremental checkpoint.
  2. On each subsequent scheduled refresh (hourly), Tableau sends SELECT * FROM events WHERE event_id > <last_max_event_id> and appends only the new rows. The filter on event_time is only applied on the initial full refresh unless you also add it to the incremental step.
  3. The Hyper file grows monotonically. Periodically (weekly / monthly), a full refresh is scheduled to drop old rows that fell out of the event_time window and to rebuild statistics.
  4. Common bug: if event_id is not strictly monotonic (e.g. UUIDs, or IDs allocated in bursts across nodes with clock skew), incremental refresh silently skips rows. Always confirm the key is BIGINT-monotonic before enabling.
  5. Common bug: incremental refresh does not delete or update rows; it only appends. For late-arriving or CDC-updated data, use full refresh or a merged data source with a deduplication step in Tableau Prep.

Output (extract file size over time).

Time Rows in Hyper Refresh SQL
Day 0 initial 100 M full scan with event_time >= today - 30
Day 0 + 1 h 100 M + 50 K event_id > 100000000
Day 0 + 2 h 100 M + 100 K event_id > 100050000
Day 7 nightly full 100 M (window slide) full scan again

Rule of thumb. Use incremental refresh for append-only fact tables. Use full refresh for slowly-changing dimensions and CDC-updated tables. Schedule a weekly full refresh even on incremental sources to compact the Hyper file and rebuild statistics.

Senior interview question on Tableau performance

A senior interviewer might ask: "Your Tableau dashboard on a Live connection loads in 22 seconds. Users are furious. Walk me through the 5 diagnostic steps you take, and how you decide between fixing the SQL, adding an extract, or moving the aggregate into the warehouse."

Solution Using performance-recording diagnosis + extract vs materialised view

Tableau performance diagnosis — 5 steps

1. Turn on Performance Recording; reproduce the slow dashboard load.
2. Open the recording workbook; identify the top slow "Executing Query" event.
3. Copy the emitted SQL; run it directly against the warehouse with EXPLAIN.
4. Decide the fix:
   - SQL is fast in warehouse but Tableau is slow → network / Hyper overhead
   - SQL is slow in warehouse                    → add index / partition / cluster / materialise
   - Repeated same-SQL from many marks           → dashboard has too many worksheets; consolidate
5. Choose the durable fix:
   - High viewership + stale-OK data             → switch to Extract (Hyper)
   - Fresh-data-mandatory + expensive SQL        → materialised view / Snowflake dynamic table
   - Complex calc field                           → push logic into a view / dbt model
Enter fullscreen mode Exit fullscreen mode

Step-by-step trace.

Step Finding Action
Perf recording 18 s in one Executing Query isolate the SQL
EXPLAIN in Snowflake full-scan on 500M rows, 12 s needs aggregate
Repeat count dashboard has 4 worksheets emitting the same SQL consolidate
Freshness need data OK if 1 h old extract eligible
Fix Snowflake materialised view + Tableau Extract with 1 h refresh 22 s → 1.4 s

The diagnosis is always the same: get the SQL, prove it's slow in the warehouse, then decide whether to fix the warehouse, add an extract, or restructure the dashboard.

Output:

Metric Before After
Dashboard load p95 22 s 1.4 s
Warehouse queries per load 4 0 (extract answers all four)
Extract refresh cost 1 warehouse query / hour
Refresh latency live up to 1 h

Why this works — concept by concept:

  • Performance Recording is the ground truth — Tableau's Performance Recording captures every VizQL, SQL, and rendering event with duration. Any performance guess without a recording is a guess.
  • The SQL is the fix surface — everything Tableau does that touches a warehouse eventually becomes SQL. Fixing the SQL (indexes, aggregates, materialised views) or fixing the SQL's execution path (extract vs live) is the whole optimisation surface.
  • Extract is a Hyper snapshot — Extract mode replaces the warehouse round-trip with a Hyper query. Wins when the freshness budget is > refresh cadence and viewership is > refresh count.
  • Materialised aggregates live in the warehouse — for fresh-data-mandatory dashboards, the aggregate should live in Snowflake (materialised view, dynamic table, dbt incremental model). Tableau then queries the aggregate.
  • Cost — Extract is O(refresh) cost. Live is O(viewership) cost. Materialised aggregates are O(refresh) warehouse cost + O(viewership) warehouse cost on the aggregate table, which is 100–1000x cheaper than on the raw fact.

SQL
Topic — aggregation
Dashboard aggregate SQL practice

Practice →

SQL Topic — SQL · medium Medium SQL for BI dashboards

Practice →


3. Power BI — Microsoft integration, DAX + Fabric

power bi vs tableau — Power BI is the DAX + Vertipaq engine that lives on Fabric OneLake and wins in every Microsoft shop

The mental model in one line: Power BI is a report canvas backed by a Vertipaq columnar in-memory engine, where DAX (not SQL) is the measure language, Import loads compressed columnar data into Vertipaq and Direct Query pushes down to the warehouse, and Microsoft Fabric OneLake is the underlying data foundation as of 2024. Once you say "Vertipaq answers, DAX defines, Fabric stores," the entire Power BI interview surface becomes a deduction from those three moving parts.

Iconographic Power BI diagram — a report canvas on the left with three chart tiles, a blue Vertipaq columnar cabinet in the middle labelled with a DAX formula ribbon, an Import vs Direct Query toggle beneath, and a Fabric OneLake cylinder on the right connected by a Composite-Model bracket.

The three core abstractions.

  • Report + visual. The report is what the analyst builds in Power BI Desktop (.pbix). It contains pages with visuals bound to fields in a shared dataset (semantic model).
  • Dataset (semantic model). The published data model — tables, relationships, measures, calculated columns, RLS rules. Lives in a Power BI workspace. Post-2023 the preferred term is "semantic model".
  • Dataflow / Fabric Lakehouse. The ETL layer. Dataflows are Power Query pipelines that materialise into the dataset; in Fabric, Lakehouses store Delta Parquet in OneLake, and datasets query them via Direct Lake mode.

DAX — the measure language, not SQL.

  • DAX (Data Analysis eXpressions) is a functional language with SUM, CALCULATE, FILTER, RELATED, SUMX, RANKX, TIMEINTELLIGENCE functions.
  • Measures — dynamic, evaluated at query time in the visual's filter context. Do not consume model memory.
  • Calculated columns — evaluated at refresh time; stored in Vertipaq. Cheap at query time, expensive at refresh.
  • Calculation groups — reusable measure "templates" (e.g. "YTD", "PY", "MoM %") applied across many base measures without repeating logic.

Vertipaq — the columnar in-memory engine.

  • Column-store with dictionary encoding, RLE, bit-packing. Typical 10–100x compression over raw Parquet.
  • Formula Engine (single-threaded) evaluates DAX expressions; Storage Engine (multi-threaded) scans Vertipaq columns.
  • Model size is memory-bound: Premium capacities give you 10–400 GB model memory per capacity, depending on SKU.

Direct Query vs Import vs Composite models — the execution split.

  • Import. Data loaded into Vertipaq at refresh time. Fast queries, stale data, memory-bounded.
  • Direct Query. Every visual query compiles DAX → source-dialect SQL and sends to the source. Fresh data, warehouse-billable, slower per query.
  • Composite models. Some tables Import, some Direct Query. Aggregation tables provide a fast Import layer over a slow Direct Query fact table.
  • Direct Lake (Fabric). Reads Delta Parquet from OneLake without importing into Vertipaq — best of both worlds: fresh data, columnar-speed queries, no explicit refresh.

Fabric integration — the 2024 rewrite.

  • Microsoft Fabric merges Power BI Premium capacity with Synapse, Data Factory, Data Engineering, Data Warehouse, and Real-Time Analytics workloads.
  • OneLake — the single Fabric-wide storage layer. Every workload reads/writes Delta Parquet in OneLake.
  • Direct Lake mode — Power BI datasets query OneLake tables directly. No import, no refresh, near-Vertipaq speed.
  • Fabric capacity units (F-SKUs) replace the old Premium P-SKUs; pay per capacity per hour, pausable.

Copilot for Power BI — the AI layer.

  • Natural-language "explain this visual", "suggest a measure", "narrative summary of this page".
  • DAX generation assist in Desktop.
  • Requires Fabric F64+ capacity (not available on Pro-only tenants).

Pricing — per-user + capacity, both.

  • Power BI Pro — around $10 / user / month. Personal + small-team sharing.
  • Power BI Premium Per User (PPU) — around $20 / user / month. Adds paginated reports, larger datasets, Copilot.
  • Fabric capacity (F-SKU) — from ~$150/month (F2) to $80K+/month (F2048). Flat rate, shared across the workspace. Viewers on Fabric-backed workspaces need Pro but no per-viewer premium fee.

Common interview probes on Power BI.

  • "What is the difference between a measure and a calculated column?" — measure = dynamic, query-time; column = static, refresh-time.
  • "When would you use Direct Query instead of Import?" — fresh-data-mandatory, model too large to fit in memory, real-time dashboards.
  • "What is a composite model?" — mixes Import + Direct Query on one dataset; aggregation tables + fact Direct Query is the canonical pattern.
  • "How does Fabric change Power BI?" — Fabric is the platform; Power BI is a workload on top; OneLake is the storage; Direct Lake is the new default fresh-data mode.

Worked example — measure vs calculated column on the same problem

Detailed explanation. An analyst wants to compute total revenue net of a 10% commission. It looks trivial — but the choice between a measure and a calculated column has real performance and correctness implications, and interviewers love to probe it.

Question. Given a sales(amount) fact table, define "Net Revenue = amount * 0.9" both as a DAX measure and as a calculated column. Show when each is correct and what the performance implications are.

Input. sales table.

sale_id region amount
1 NA 100
2 NA 200
3 EU 300

Code.

// Option A — DAX measure (dynamic, query time)
NetRevenue :=
CALCULATE (
    SUMX ( sales, sales[amount] * 0.9 )
)

// Option B — calculated column (static, refresh time)
sales[net_amount] = sales[amount] * 0.9

// Option C — the cleanest measure, using an existing SumOfAmount measure
NetRevenue2 := [SumOfAmount] * 0.9
Enter fullscreen mode Exit fullscreen mode

Step-by-step explanation.

  1. Option A (measure with SUMX) evaluates row-by-row at query time, multiplying amount * 0.9 for each row in filter context, then summing. Correct for any filter context (region, product, time), but expensive on large tables.
  2. Option B (calculated column) computes net_amount once per row at refresh time and stores it in Vertipaq. Query time is a simple SUM(net_amount) — very fast. Costs Vertipaq memory equal to the number of rows.
  3. Option C (simple measure over an existing measure) is the cleanest for constant multipliers — [SumOfAmount] * 0.9. It doesn't need SUMX because the multiplier is a scalar constant, not a per-row expression. Fastest and most maintainable.
  4. Correctness caveat: if the commission rate varies per row (e.g. commission depends on region), Option C is wrong — you must use SUMX(sales, sales[amount] * sales[commission_rate]).
  5. Rule: measures are correct in every filter context; calculated columns "freeze" a value at refresh time. Use measures unless the value is truly static per row.

Output (all three options produce the same total for a flat 10% commission).

region sum_amount net_revenue
NA 300 270
EU 300 270
Total 600 540

Rule of thumb. Default to measures. Use calculated columns only when the value is truly static per row and query time on SUMX is a proven bottleneck. Never use calculated columns for per-row values that depend on filter context — you will get wrong numbers.

Worked example — composite model with aggregation table

Detailed explanation. A fact table events has 1B rows in Snowflake. Loading all 1B into Vertipaq blows the memory budget; Direct Query on every visual load is too slow. The composite-model pattern solves it: import a pre-aggregated table for fast queries, Direct Query the raw fact only when the user drills down.

Question. Design a Power BI composite model over a 1B-row events fact in Snowflake with a Vertipaq-imported daily aggregation table. Show the model, the aggregation mapping, and what happens when a user drills from month → day → event.

Input.

Table Storage mode Row count
events (raw) Direct Query 1,000,000,000
events_daily_agg Import (Vertipaq) 3,000,000
dim_date Dual 3,650
dim_event_type Dual 40

Code.

-- 1. Create the aggregation table in Snowflake first
CREATE OR REPLACE TABLE events_daily_agg AS
SELECT
    DATE_TRUNC('day', event_time) AS event_date,
    event_type,
    COUNT(*)                      AS event_count,
    SUM(revenue)                  AS revenue
FROM events
GROUP BY 1, 2;

-- 2. In Power BI Desktop, add both tables to the model
-- 3. Set events (fact) to Direct Query, events_daily_agg to Import
-- 4. Manage Aggregations wizard:
--       events_daily_agg[event_count]  ← COUNT(events)
--       events_daily_agg[revenue]      ← SUM(events[revenue])
--       Grouping: events_daily_agg[event_date] ← DATE(events[event_time])
--                 events_daily_agg[event_type] ← events[event_type]
Enter fullscreen mode Exit fullscreen mode

Step-by-step explanation.

  1. When a user views a monthly summary visual (SUM(revenue) by month), the DAX engine detects the query can be answered from events_daily_agg (Import) and runs at Vertipaq speed — sub-second.
  2. When the user drills to daily granularity, still answered from events_daily_agg — still fast.
  3. When the user drills to individual event_id, the aggregation table cannot answer (event_id is not in the agg). DAX transparently falls back to Direct Query against events in Snowflake.
  4. The user experience is uniform: fast at rollup levels, warehouse-hit only when required. The Vertipaq memory footprint stays tiny (3M rows vs 1B).
  5. The pattern requires disciplined aggregation table maintenance: every time you add a new dimension or measure to the fact, refresh the agg table's schema and re-import.

Output (per-query storage mode and latency).

User action Answered by Latency
Monthly rollup Vertipaq (agg) ~100 ms
Daily granularity Vertipaq (agg) ~200 ms
Drill to event_id Direct Query (fact) 3–10 s
Filter by dim_date Vertipaq (dual) ~50 ms

Rule of thumb. For any fact table above ~200M rows, design a composite model with an Import aggregation table + Direct Query fact from day one. Fabric's Direct Lake mode is progressively replacing this pattern by giving Vertipaq-speed queries over OneLake Delta tables without an explicit import step — but composite models remain the standard on non-Fabric stacks.

Senior interview question on Power BI at scale

A senior interviewer might ask: "You are moving a Power BI tenant from Premium P1 capacity to Fabric F64 with OneLake. What changes for your data engineers, your analysts, and your ops team? Where can this migration go wrong?"

Solution Using a Fabric migration checklist + OneLake shortcut pattern

Power BI Premium P1 → Fabric F64 migration
==========================================

For data engineers
------------------
1. Enable OneLake on the tenant; provision Fabric workspaces on F64 capacity.
2. Migrate ETL from Data Factory / ADF to Fabric Data Factory OR link
   existing ADF via OneLake shortcut (no data movement).
3. Land data as Delta Parquet in OneLake (Lakehouse or Warehouse item).
4. Convert selected datasets from Import → Direct Lake mode.
5. Deprecate the manual refresh schedule for those datasets.

For analysts
------------
6. Continue building in Power BI Desktop; publish to Fabric-backed workspace.
7. When binding to a Direct Lake dataset, no refresh schedule needed —
   OneLake is the source of freshness.
8. Copilot for Power BI becomes available (F64 minimum for Copilot).

For ops team
------------
9. Fabric capacity is pausable — schedule pause overnight to cut costs.
10. Governance shifts to OneLake data lineage; workspace-level admin still
    lives in Power BI admin portal.
11. Monitoring: Fabric Capacity Metrics app for CU-hours by workload.
Enter fullscreen mode Exit fullscreen mode

Step-by-step trace.

Concern Before (P1) After (F64)
Capacity SKU P1 (fixed 8 v-cores) F64 (64 CU, pausable)
Storage dataset memory only OneLake Delta Parquet
Refresh mode Import + scheduled refresh Direct Lake (no refresh)
ETL tool ADF external Fabric Data Factory (native or shortcut)
Copilot not available on P-SKU available on F64+
Governance dataset RLS + workspace roles + OneLake lineage, Purview integration

The migration succeeds when data engineers land the data as Delta in OneLake and analysts opt in to Direct Lake mode dataset by dataset. It fails when teams try to lift-and-shift Import datasets one-for-one without changing storage mode — no benefit from OneLake, same refresh costs, same memory pressure.

Output:

Metric P1 F64 (post-migration)
Max dataset size 100 GB (Premium) 400 GB (F64)
Refresh cadence for Direct Lake manual continuous
Copilot access none full
Pausable capacity no yes (nights + weekends)
ETL integration external native

Why this works — concept by concept:

  • Fabric is the platform, Power BI is a workload — after 2024, buying Power BI at scale = buying Fabric. The mental model that "Power BI Premium capacity" is a self-contained thing no longer holds; capacity is Fabric-wide.
  • OneLake is the storage foundation — every Fabric workload reads/writes Delta Parquet in OneLake. Direct Lake datasets query OneLake directly, eliminating the Import refresh loop.
  • Direct Lake replaces Import for large datasets — no explicit refresh, no Vertipaq memory pressure per import, near-Vertipaq speed. Comes with restrictions (some DAX patterns fall back to Direct Query), but it is the future default.
  • Copilot gating — Copilot for Power BI requires Fabric F64+ capacity. Teams that stay on P-SKU or Pro cannot access it. This is a real reason to migrate to Fabric even without OneLake benefits.
  • Cost — F-SKUs are hourly, pausable, and re-billable. A 60% night/weekend pause on F64 is a real 30–40% capacity cost reduction; P-SKUs are monthly-flat and cannot pause.

SQL
Topic — window-functions
Window-function problems for DAX-style measures

Practice →

SQL Topic — joins Join problems that mirror Power BI relationships

Practice →


4. Looker — LookML semantic layer, code-first governance

looker vs tableau — Looker is the code-first BI tool where LookML is the semantic layer, every metric change is a pull request, and PDTs materialise the heavy lifts

The mental model in one line: a Looker project is a git repo of .lkml files defining views, models, and explores; every measure, dimension, and join is version-controlled; queries push down to the warehouse; and PDTs (persistent derived tables) materialise expensive SQL back into the warehouse for speed. Once you say "LookML is the semantic layer, git is the change control, PDTs are the cache," the entire looker vs tableau interview surface becomes a deduction from those three properties.

Iconographic Looker LookML diagram — a code-scroll on the left labelled 'view · model · explore' with dimension and measure ribbons, a git-branch fork-glyph in the middle showing a pull-request bubble, a green PDT persistent-derived-table cylinder on the right, and a Google-cloud badge floating at the top-right.

The three core LookML files.

  • View (.view.lkml). Wraps a single warehouse table (or derived table). Defines dimensions (columns you can group / filter by) and measures (aggregates like SUM, COUNT, AVG).
  • Model (.model.lkml). Declares the connection, the included views, and the explores — which views can join to which, and how.
  • Explore. The user-facing entry point in the Looker UI. An explore is "a view + everything it can join to". Business users pick an explore, drag dimensions and measures, and Looker generates SQL.

LookML by example — the minimum semantic model.

  • Each dimension has a type (string, number, date, yesno, tier), a sql: expression referencing ${TABLE}.column, and optional label, description, hidden.
  • Each measure has a type (sum, count, average, count_distinct, running_total, ...), a sql: expression, and optional filters, value_format_name.
  • Each explore lists its joins with sql_on: and relationship: (many_to_one, one_to_many, many_to_many, one_to_one).

The git-based dev flow.

  • Every LookML project is backed by a git repo (GitHub / GitLab / Bitbucket / Azure DevOps).
  • Developers work in development mode on a personal branch; run "LookML Validator" before committing.
  • Commits open a pull request; teammates review; merge deploys to production mode.
  • Every dashboard using the changed measure updates atomically at merge time.

Explores and joins — the query surface.

  • The user picks an explore, then Looker exposes every field from every joined view.
  • Joins are declared once in the model file and reused across every dashboard.
  • Symmetric aggregates prevent double-counting in fan-out joins — Looker's SQL includes a subquery for the fanned-out side to keep SUM(fact.amount) correct even in the presence of a one_to_many join.

PDT — persistent derived tables.

  • A derived_table block in a view lets you define a SQL query as the "table" for that view.
  • Adding persist_for: "6 hours" or sql_trigger_value: promotes it to a PDT — Looker materialises the SQL as a real table in the warehouse and refreshes it on a schedule or a trigger value change.
  • Downstream explores query the PDT (cheap) instead of re-running the derived SQL every time (expensive).

Google acquisition and the Looker / Looker Studio Pro split.

  • Google acquired Looker in 2019.
  • Looker (classic) — enterprise LookML + embed + full modeling. This is what most enterprise data teams call "Looker".
  • Looker Studio (formerly Google Data Studio) — free-tier, drag-drop, no LookML. Rebranded in 2022.
  • Looker Studio Pro — paid tier of Looker Studio with team workspaces, SLAs, Cloud Identity integration.
  • They share brand only. Interview trap: some candidates conflate them.

Embedded analytics — Looker's strongest suit.

  • Embed SDK for JavaScript apps — sign an embed URL server-side, drop an iframe, filter by user attributes.
  • Powered by Looker — the productisation of embed for SaaS vendors selling customer-facing analytics.
  • Row-level security via user attributes maps cleanly to per-customer views inside a single dataset.

Common interview probes on Looker.

  • "What is a LookML view vs model vs explore?" — view = table wrapper; model = connection + explores; explore = a joined-view surface for the UI.
  • "How does symmetric aggregation work?" — Looker rewrites SUM(fact.amount) to a subquery-first-then-aggregate pattern when a join fans out.
  • "When would you use a PDT?" — when a derived table's SQL is expensive and reused; persist_for sets a TTL, sql_trigger_value refreshes on an upstream change.
  • "How is Looker priced?" — platform fee + per-developer + per-viewer, negotiated by contract.

Worked example — a full LookML view + explore for a Sales dataset

Detailed explanation. The idiomatic Looker starter — a sales view with a few dimensions and measures, a dim_date view, and an explore that joins them. This is the shape every real LookML project takes.

Question. Write the sales.view.lkml, dim_date.view.lkml, and ecommerce.model.lkml for a Snowflake sales fact table joined to dim_date. Include a total-revenue measure, a distinct-customer measure, and an average-order-value measure.

Input. sales fact table and dim_date dimension.

sale_id date_key customer_id amount
1 20260701 c1 100
2 20260701 c1 50
3 20260701 c2 200
4 20260702 c3 300

Code.

# views/sales.view.lkml
view: sales {
  sql_table_name: warehouse.public.sales ;;

  dimension: sale_id {
    primary_key: yes
    type: number
    sql: ${TABLE}.sale_id ;;
  }
  dimension: date_key {
    type: number
    hidden: yes
    sql: ${TABLE}.date_key ;;
  }
  dimension: customer_id {
    type: string
    sql: ${TABLE}.customer_id ;;
  }
  dimension: amount {
    type: number
    sql: ${TABLE}.amount ;;
    value_format_name: usd
  }

  measure: total_revenue {
    type: sum
    sql: ${amount} ;;
    value_format_name: usd
    description: "Sum of sale amount, in USD."
  }
  measure: unique_customers {
    type: count_distinct
    sql: ${customer_id} ;;
    description: "Distinct customers with at least one sale."
  }
  measure: aov {
    type: number
    sql: 1.0 * ${total_revenue} / NULLIF(${unique_customers}, 0) ;;
    value_format_name: usd
    description: "Average order value = total_revenue / unique_customers."
  }
}
Enter fullscreen mode Exit fullscreen mode
# views/dim_date.view.lkml
view: dim_date {
  sql_table_name: warehouse.public.dim_date ;;

  dimension: date_key {
    primary_key: yes
    type: number
    hidden: yes
    sql: ${TABLE}.date_key ;;
  }
  dimension_group: date {
    type: time
    timeframes: [date, week, month, quarter, year]
    sql: ${TABLE}.date ;;
  }
}
Enter fullscreen mode Exit fullscreen mode
# models/ecommerce.model.lkml
connection: "snowflake_prod"
include: "/views/*.view.lkml"

explore: sales {
  label: "Sales analytics"
  join: dim_date {
    type: left_outer
    relationship: many_to_one
    sql_on: ${sales.date_key} = ${dim_date.date_key} ;;
  }
}
Enter fullscreen mode Exit fullscreen mode

Step-by-step explanation.

  1. The sales view wraps warehouse.public.sales. Every column is either a dimension (groupable / filterable) or a measure (aggregate). Referencing ${amount} inside the total_revenue measure is a LookML reference — it expands to sales.amount at SQL generation.
  2. aov shows the LookML compose pattern — a measure can reference other measures. NULLIF guards against division by zero. value_format_name controls display formatting.
  3. The dim_date view uses dimension_group: date with timeframes: — this auto-generates date_date, date_week, date_month, date_quarter, date_year dimensions from one column. Analysts pick the granularity in the UI.
  4. The model file declares the Snowflake connection, includes every view file, and declares the sales explore that left-outer-joins dim_date on date_key. This explore is what the user picks in the Looker UI.
  5. When a user drags date_month and total_revenue, Looker emits SELECT DATE_TRUNC('month', dim_date.date) AS date_month, SUM(sales.amount) AS total_revenue FROM sales LEFT JOIN dim_date USING (date_key) GROUP BY 1 ORDER BY 1.

Output (a date_month × total_revenue × aov query).

date_month total_revenue unique_customers aov
2026-07-01 650 3 216.67

Rule of thumb. Design views around tables (one view per real table). Design explores around business questions (one explore per analytical use case). Never bury business logic in a view; put every measure into LookML with a description so the field-picker doubles as documentation.

Worked example — PDT with sql_trigger_value for a heavy aggregation

Detailed explanation. A daily_active_users metric requires a heavy COUNT(DISTINCT user_id) over 30 days of event data. Running it on every dashboard load is punishing on Snowflake credit. A PDT with sql_trigger_value materialises the aggregate and refreshes only when new data has landed.

Question. Write a LookML view backed by a derived table computing daily active users over 30 days, persisted as a PDT that refreshes when new events arrive.

Input. events table with event_time and user_id.

Code.

# views/daily_active_users.view.lkml
view: daily_active_users {
  derived_table: {
    sql:
      SELECT
        DATE_TRUNC('day', event_time)     AS active_date,
        COUNT(DISTINCT user_id)           AS dau
      FROM   warehouse.events
      WHERE  event_time >= CURRENT_DATE - 30
      GROUP BY 1
    ;;
    sql_trigger_value: SELECT MAX(event_time)::date FROM warehouse.events ;;
    distribution_style: even     # Snowflake / Redshift materialisation hint
    indexes: ["active_date"]     # Postgres / MySQL
  }

  dimension: active_date {
    type: date
    sql: ${TABLE}.active_date ;;
  }
  measure: dau {
    type: sum
    sql: ${TABLE}.dau ;;
    description: "Daily active users, distinct user_id per day."
  }
}
Enter fullscreen mode Exit fullscreen mode

Step-by-step explanation.

  1. The derived_table.sql: block defines the query that produces the derived rows. Looker executes it once and creates a real table in the warehouse (schema LOOKER_SCRATCH.<hash>_daily_active_users).
  2. sql_trigger_value runs periodically (Looker's PDT scheduler, every 5 min by default). When the returned value changes (e.g. MAX(event_time) advances to a new day), Looker rebuilds the PDT with the derived SQL.
  3. Downstream explores that include this view query the PDT directly. The heavy COUNT DISTINCT over 30 days runs once per trigger change, not per dashboard load.
  4. distribution_style and indexes are warehouse-specific hints Looker uses when creating the materialised table.
  5. Cost math. Without PDT: COUNT DISTINCT over 30 days × N dashboard loads/day = N heavy queries. With PDT: 1 heavy query per trigger + N tiny queries against the PDT. For N=500 daily loads and a trigger change once/day, warehouse cost drops from 500 heavy queries to 1 heavy + 500 tiny.

Output (materialised PDT rows).

active_date dau
2026-07-15 12500
2026-07-16 12780
2026-07-17 13010

Rule of thumb. Use persist_for for time-based TTL (e.g. "6 hours"). Use sql_trigger_value for change-based refresh (e.g. "when the source's max updated_at changes"). Use datagroup_trigger for shared refresh signals across many PDTs. Always add a description to the PDT view so downstream users know the cache TTL.

Senior interview question on Looker migration from Tableau

A senior interviewer might ask: "Your company decided to move from Tableau to Looker because of metric drift complaints. Walk me through the migration plan — what happens to the existing Tableau workbooks, how do you handle the semantic layer, and how do you not lose the analysts during the switch?"

Solution Using a phased LookML-first migration + analyst training track

Tableau → Looker migration — phased plan (~6 months)

Phase 1 — Semantic layer first (weeks 1–4)
------------------------------------------
1. Inventory every measure across top 20 Tableau workbooks.
2. Reconcile drifted definitions with finance / product owners.
3. Model canonical measures into LookML views (1 per warehouse table).
4. Ship LookML v1.0 to a dev branch; validate against a sample workbook.

Phase 2 — Explores + parity dashboards (weeks 5–10)
---------------------------------------------------
5. Build one Looker explore per top-tier Tableau workbook.
6. Rebuild the top 5 dashboards in Looker; compare numbers side-by-side.
7. Materialise heavy aggregates as PDTs; tune warehouse costs.

Phase 3 — Analyst training + rollout (weeks 11–18)
--------------------------------------------------
8. Weekly LookML office hours + 2-hour analyst training.
9. Freeze new Tableau workbook creation; migrate long-tail on demand.
10. Deprecate the Tableau license after 90 days of parity.

Phase 4 — Governance + embed (weeks 19–24)
------------------------------------------
11. Introduce PR review requirement on LookML changes.
12. Roll out embedded dashboards to customer-facing product.
13. Retire Tableau Server; run cost + drift retrospective.
Enter fullscreen mode Exit fullscreen mode

Step-by-step trace.

Phase Deliverable Success signal
1 LookML v1.0 with canonical measures one revenue definition, reviewed
2 Top-5 dashboards ported numbers match Tableau exactly
3 Analyst training complete analysts self-serve in Looker
4 Governance + embed live PRs required, embed shipping

The migration succeeds when the semantic layer lands before the dashboards. Teams that flip the order — port dashboards first, semantic layer later — end up with the same drift they wanted to escape.

Output:

Metric Tableau baseline Looker post-migration
Metric definitions per KPI 3–4 per KPI 1 per KPI (LookML)
Time to add a new dimension analyst edits workbook modeler opens PR, reviewed
Cross-dashboard consistency drift monthly atomic — merge updates all
Warehouse cost uncached repeated queries PDT cached hot aggregates
License cost per-viewer × 5,000 platform + per-editor + free viewer

Why this works — concept by concept:

  • Semantic layer first, dashboards second — LookML is the whole point of moving to Looker. Migrating dashboards without the LookML discipline just recreates the drift on a new tool.
  • PR-based governance — every measure change is a diff, reviewed by a modeler. This is what prevents drift at the source; it also creates the audit trail interviewers ask about.
  • PDT materialisation replaces extracts — Tableau's Hyper extracts move to warehouse-native PDTs. The refresh signal is the warehouse's own change (via sql_trigger_value), not a Tableau Server schedule.
  • Embedded analytics is a step-4 payoff — Looker's embed SDK is often the reason product teams push for the migration. It comes last because it depends on stable LookML + explores.
  • Cost — the migration is 6 months of senior modeler + analyst time. The payoff is one metric definition per KPI (drift goes to zero), cheaper warehouse (PDTs replace re-runs), and a viable embed story. A Tableau → Looker migration that fails is almost always one that skipped the semantic-layer discipline.

SQL
Topic — joins
LookML-style join and aggregation problems

Practice →

SQL Topic — window-functions Window functions for LookML measures

Practice →


5. Sigma — spreadsheet-first cloud BI

sigma vs looker — Sigma is the spreadsheet-first BI tool where cells compile to SQL, pushdown is default, and input tables let you write back to the warehouse

The mental model in one line: a Sigma workbook is a spreadsheet-shaped UI over your cloud warehouse (Snowflake / BigQuery / Databricks / Redshift), where every column is a live SQL query pushed down to the warehouse and every formula compiles to SQL, with input tables letting you write rows back into the warehouse from the same workbook. Once you say "spreadsheet UX, pushdown SQL, warehouse-native writeback," the entire sigma vs looker interview surface becomes a deduction from those three properties.

Iconographic Sigma diagram — a spreadsheet-grid workbook card on the left with a bright pivot cell, an orange formula-translator wheel in the middle turning a cell formula into a SQL scroll, a blue Snowflake-shaped cloud warehouse cabinet on the right, and a purple input-table writeback arrow curving back from workbook to warehouse.

The four core abstractions.

  • Workbook. The top-level document (analogous to an Excel workbook). Contains pages.
  • Page. A canvas that holds elements. Analogous to a spreadsheet tab or a Looker dashboard.
  • Element. The unit of visualization or interaction — table, pivot table, chart, control, input table.
  • Column / formula. Every column in a table/pivot has a formula, expressed in Sigma's spreadsheet-like formula language (Sum([col]), Count([col]), If(...)).

Pushdown SQL — always, no extract.

  • Every workbook operation compiles to SQL and executes against the connected warehouse. There is no extract, no in-memory cache the size of a dataset.
  • Sigma uses the warehouse's own result cache (Snowflake result cache, BigQuery cached results, Databricks Delta cache) as the only cache layer.
  • Cost model: every workbook load = warehouse credit spent. Rely on the warehouse's caching + Sigma's smart materialisation for cost control.

Spreadsheet UX — the differentiator.

  • Analysts familiar with Excel pivot tables can be productive in hours, not weeks.
  • Formulas look like Sum([Amount]), If([Region] = "NA", 1, 0), RunningSum([Sales], [Date]) — the same formula grammar as Excel with warehouse-scale under the hood.
  • Pivot elements compile to GROUP BY + optionally PIVOT SQL.

Input tables — writeback in the workbook.

  • A first-class element type. Users can type values into cells (like a spreadsheet), and Sigma writes them to a real table in the warehouse.
  • Use cases: forecasting overrides, budget entry, tag / label enrichment, manual data cleanup workflows.
  • The writeback table is a real warehouse table — every other BI tool and every SQL analyst can read it. This is a genuinely differentiated feature; it decouples "data entry" from a separate app.

Permission and RLS model.

  • Row-level security via warehouse permissions + Sigma's own user attribute filters.
  • Column-level masking supported via warehouse policies (Snowflake dynamic data masking, BigQuery policy tags).
  • Workspace / folder / workbook-level sharing controls.

Sigma vs Looker — the comparison table.

Axis Sigma Looker
Authorship analyst in spreadsheet modeler in LookML (git)
Semantic layer Sigma Dataset (optional) LookML (mandatory)
Execution pushdown only pushdown + PDT
Governance workbook + dataset git-native
Writeback first-class (input tables) requires custom action
Embed supported industry-leading
Warehouse coupling tight (Snowflake / BQ / DBX) broader
AI features Sigma AI, chat-to-formula Looker AI, natural-language explores

Comparison across all four tools.

Axis Tableau Power BI Looker Sigma
Semantic layer workbook calcs / data source shared dataset (DAX) LookML (git) Sigma dataset (optional)
Cost model per-Creator / Explorer / Viewer per-user + Fabric capacity platform + per-editor + free viewer per-user + warehouse compute
Dev flow Desktop, workbook file Desktop, workspace publish LookML in git + PR web workbook + optional dataset
Execution Extract (Hyper) or Live Import (Vertipaq) / Direct Query / Direct Lake pushdown + PDT pushdown only
AI Tableau AI, Pulse, Einstein Copilot for Power BI (F64+) Looker AI Sigma AI
Warehouse-native with Live with Direct Query / Direct Lake native pushdown native pushdown
Embed Tableau Embedded Power BI Embedded industry-leading Embed SDK supported
Writeback limited limited requires action first-class input tables

Common interview probes on Sigma.

  • "How is Sigma different from Looker?" — spreadsheet UX + no LookML + pushdown only + input tables.
  • "What is an input table?" — a workbook element that writes back to a real warehouse table; used for forecasts, overrides, manual data.
  • "How does Sigma control warehouse cost?" — result cache + materialisation for expensive aggregates + user-education on query patterns.
  • "When would you pick Sigma over Looker?" — Snowflake-first shop with strong analyst culture, no dedicated modeler headcount, writeback is a real need.

Worked example — pivot table compiled to SQL

Detailed explanation. The classic Sigma workflow — an analyst drags dimensions and measures into a pivot element. Sigma compiles the pivot to a GROUP BY (and optionally a PIVOT clause) and pushes down to the warehouse. Understanding this compilation is the mental model for Sigma performance.

Question. Given a sales table in Snowflake, show the Sigma pivot spec and the SQL it emits for "Revenue by region, product category".

Input. sales table.

sale_id region product_category revenue
1 NA electronics 500
2 NA apparel 200
3 EU electronics 300
4 EU apparel 400
5 APAC electronics 700

Code.

# Sigma pivot spec (workbook UI)
Pivot element:
  Rows:         [region]
  Columns:      [product_category]
  Values:       Sum([revenue])
  Show totals:  rows + columns
Enter fullscreen mode Exit fullscreen mode
-- SQL that Sigma emits to Snowflake
SELECT
    region,
    SUM(CASE WHEN product_category = 'electronics' THEN revenue END) AS "electronics",
    SUM(CASE WHEN product_category = 'apparel'     THEN revenue END) AS "apparel",
    SUM(revenue) AS "Total"
FROM  warehouse.sales
GROUP BY region
ORDER BY region;
Enter fullscreen mode Exit fullscreen mode

Step-by-step explanation.

  1. The user drags [region] to Rows, [product_category] to Columns, Sum([revenue]) to Values. Sigma builds an internal element spec.
  2. On workbook render, Sigma's SQL compiler translates the pivot to a GROUP BY region with SUM(CASE WHEN ...) conditional aggregates for each unique column value. This is the standard "unpivoted pivot" pattern; more modern warehouses support the PIVOT operator, and Sigma emits that on dialects that support it.
  3. The SQL is sent to Snowflake. Snowflake's result cache stores the result set for 24 h if the underlying data is unchanged.
  4. Adding a filter (e.g. Date >= '2026-01-01') adds a WHERE clause. Adding a sort adds an ORDER BY. Every workbook operation is a SQL edit.
  5. Performance tuning = Snowflake tuning (clustered by region, materialised aggregate, larger warehouse) — same as every other pushdown BI tool.

Output.

region electronics apparel Total
APAC 700 0 700
EU 300 400 700
NA 500 200 700

Rule of thumb. For any Sigma performance investigation, capture the SQL from Sigma's Query History (or Snowflake's Query History filtered by Sigma warehouse), run EXPLAIN in Snowflake, and treat it as any other query. There is no Sigma-specific tuning — only warehouse tuning.

Worked example — input table for a manual forecast override

Detailed explanation. A finance analyst needs to type in monthly revenue forecasts and blend them with actuals from the warehouse. Every other BI tool would require a separate app (Airtable, Sheets, a bespoke Django form) writing to the warehouse. Sigma does it in the same workbook via input tables.

Question. Design a Sigma workbook that shows actual revenue from sales and lets the analyst type monthly forecast overrides, with the workbook joining actuals + forecasts and computing variance.

Input. sales fact table + a Sigma input table forecast_overrides that writes to warehouse.sigma_writeback.forecast_overrides.

Code.

# Sigma workbook design

Element 1 — table "Actuals":
  Source:  warehouse.sales
  Columns: [Month] = TruncDate("month", [Date])
           [Actual] = Sum([Amount])
  Group by: Month

Element 2 — input table "Forecast overrides":
  Target table: warehouse.sigma_writeback.forecast_overrides
  Columns:
    [Month]    — user-selectable dropdown of months
    [Forecast] — user-typed number (edited in the workbook)

Element 3 — pivot table "Variance":
  Source: Join(Actuals, Forecast overrides, on Month, LEFT)
  Columns:
    [Month]
    [Actual]
    [Forecast]
    [Variance] = [Actual] - [Forecast]
    [Variance %] = 100.0 * ([Actual] - [Forecast]) / NULLIF([Forecast], 0)
Enter fullscreen mode Exit fullscreen mode
-- Under the hood, Sigma creates warehouse.sigma_writeback.forecast_overrides
-- Every time the analyst edits a cell, Sigma runs an UPSERT:
MERGE INTO warehouse.sigma_writeback.forecast_overrides t
USING (SELECT '2026-07-01'::date AS month, 12000 AS forecast) s
ON  t.month = s.month
WHEN MATCHED     THEN UPDATE SET forecast = s.forecast
WHEN NOT MATCHED THEN INSERT (month, forecast) VALUES (s.month, s.forecast);
Enter fullscreen mode Exit fullscreen mode

Step-by-step explanation.

  1. Element 1 pulls actuals from sales, grouping by month. Standard pushdown SQL.
  2. Element 2 is the input table. Sigma provisions a real warehouse table (warehouse.sigma_writeback.forecast_overrides) with columns matching the input schema. Every cell edit issues a MERGE.
  3. Element 3 joins the actuals element with the input-table element on Month. The join happens in the warehouse (Sigma pushes the whole thing down).
  4. When the analyst types 12000 into July's forecast cell, Sigma issues the MERGE, the warehouse table gets updated, and the pivot re-renders with the new value visible everywhere.
  5. Because the writeback target is a real warehouse table, downstream Airflow / dbt jobs / other BI tools can also read it. This is the "no-app-needed" pattern that decouples data entry from application code.

Output (variance pivot).

Month Actual Forecast Variance Variance %
2026-05-01 10500 10000 500 5.0%
2026-06-01 11200 11000 200 1.8%
2026-07-01 13010 12000 1010 8.4%

Rule of thumb. Use input tables for finance / operations workflows where humans need to type numbers that mix with warehouse actuals. Do not use input tables as a substitute for a real application backend — they are optimised for tabular human input, not high-throughput automated writes.

Worked example — Snowflake cost control on high-viewership Sigma workbook

Detailed explanation. A Sigma workbook viewed 10,000 times a day on a 500M-row orders fact will incinerate Snowflake credit unless someone thinks about caching. The right pattern is to materialise the workbook's underlying aggregates as a Snowflake dynamic table (or dbt incremental) and point Sigma at that.

Question. A "revenue by day" Sigma workbook is bleeding Snowflake credits. Show the cost-cut pattern using a Snowflake dynamic table + Sigma workbook redirection.

Input. Current state: workbook queries raw orders (500M rows) on every load.

Code.

-- Snowflake dynamic table refreshed every 15 minutes
CREATE OR REPLACE DYNAMIC TABLE warehouse.revenue_by_day
TARGET_LAG = '15 minutes'
WAREHOUSE  = compute_wh
AS
SELECT
    DATE_TRUNC('day', order_ts) AS d,
    region,
    product_category,
    SUM(amount) AS revenue,
    COUNT(*)    AS orders
FROM warehouse.orders
GROUP BY 1, 2, 3;
Enter fullscreen mode Exit fullscreen mode
# Sigma workbook update
Data source: warehouse.revenue_by_day   # was: warehouse.orders
Columns:     [d], [region], [product_category], [revenue], [orders]
Element:     pivot table (rows: d, values: Sum(revenue))
Enter fullscreen mode Exit fullscreen mode

Step-by-step explanation.

  1. The dynamic table materialises the daily aggregate. Snowflake refreshes it automatically every 15 min based on the TARGET_LAG, using incremental refresh where possible.
  2. The Sigma workbook is repointed from orders (500M rows) to revenue_by_day (~3M rows for years of history). Every pivot query now scans 3M rows, not 500M.
  3. Snowflake's result cache further reduces cost — repeat loads within 24 h hit the cache free.
  4. 10,000 daily loads × 500M-row scan → 10,000 × 3M-row scan → cache-hits after the first. Credit cost drops 2–3 orders of magnitude.
  5. Trade-off: 15-min freshness instead of second-level freshness. Communicate this to the workbook consumers explicitly (put a small "Data as of: 15 min ago" caption on the workbook page).

Output (cost profile before + after).

Metric Before (raw orders) After (dynamic table)
Rows scanned per load 500 M 3 M
Warehouse credit per load high low
Daily loads 10,000 10,000
Freshness seconds 15 min
Total daily credit very high 50–100x lower

Rule of thumb. For any high-viewership pushdown BI workbook (Sigma, Looker without PDT, Tableau Live, Power BI Direct Query), the fix is to materialise the aggregate in the warehouse — dynamic table (Snowflake), materialised view (BigQuery), or dbt incremental model. The BI tool then queries the aggregate; cost drops 10x–1000x.

Senior interview question on Sigma vs Looker in a Snowflake shop

A senior interviewer might ask: "Your Snowflake-native B2B SaaS company has 200 analysts and a small data platform team. Should you pick Sigma or Looker? What are the trade-offs and what would push you to each?"

Solution Using the 5-question framework + Sigma-vs-Looker trade-offs

Sigma vs Looker — Snowflake-native B2B SaaS decision
====================================================

Q1 Source        → Snowflake only         → both eligible
Q2 Semantic      → dbt is source of truth  → both can consume via
                    dbt Semantic Layer     → both viable
                 → nothing outside BI tool → Looker (LookML enforces it)
Q3 Authorship    → 200 analysts, few modelers → Sigma leans easier
                 → strict PR-based governance → Looker required
Q4 Embed         → not mission-critical    → either
                 → customer-facing product → Looker Embed SDK
Q5 TCO shape     → per-viewer scaling      → Sigma slightly cheaper
                 → mostly-editor workforce → Looker per-editor OK

Sigma-vs-Looker trade-offs
--------------------------
Sigma
  + Spreadsheet UX — analysts productive in hours
  + Input tables for forecasts / overrides / manual data
  + No LookML learning curve
  + Warehouse-native pushdown
  − Semantic layer is optional — drift can return
  − Embed story less mature than Looker
  − Warehouse cost per view can be higher than Looker+PDT

Looker
  + LookML enforces a single source of truth per metric
  + PDT materialises expensive aggregates
  + Industry-leading Embed SDK
  + Git-based governance
  − LookML learning curve (weeks, not hours)
  − Modeler headcount required
  − Slower iteration for one-off analyst questions
Enter fullscreen mode Exit fullscreen mode

Step-by-step trace.

Requirement Answer Pushes to
Q1 — Source Snowflake only either
Q2 — Semantic dbt for canonical + BI for last-mile either
Q3 — Authorship 200 analysts, 3 modelers Sigma
Q4 — Embed dashboards for internal use only either
Q5 — TCO 5,000 viewers, 200 editors mixed — Sigma slightly ahead

Four of five answers lean Sigma (or tie); no answer strongly requires Looker. Verdict: pick Sigma for the analyst-heavy, dbt-backed shop with no embed requirement.

Flip the answers — 3 analysts, 20 modelers, customer-facing embed critical — and Looker wins unambiguously.

Output:

Verdict Reasoning
Pick Sigma for the 200-analyst Snowflake shop analyst UX + input tables + no LookML curve
Don't pick Looker here modeler headcount + LookML curve outweighs semantic benefit when dbt already sources truth
Reconsider Looker if embed becomes mission-critical or metric drift returns

Why this works — concept by concept:

  • Authorship is the deepest split — Sigma optimises for analyst-authored, spreadsheet-familiar workflows. Looker optimises for engineer-authored, code-reviewed metrics. The team you have decides which tool ships value faster.
  • Semantic layer moved to dbt — with dbt Semantic Layer / MetricFlow / Cube taking over metric definition, the BI tool's semantic-layer discipline matters less. Both Sigma and Looker can consume dbt metrics; the BI tool becomes the presentation layer.
  • Input tables are a real differentiator — the finance / ops workflows that used to spin up separate Django forms now live in the same workbook. Only Sigma has this as first-class.
  • Embed is Looker's moat — if the SaaS product needs customer-facing dashboards embedded in the app, Looker's Embed SDK is the industry standard and hard to beat.
  • Cost — Sigma's per-user + warehouse compute model can beat Looker's platform + editor model at the analyst-heavy end. At the editor-heavy end (20 modelers, 200 editors), Looker often wins. Model both scenarios with real vendor quotes before signing.

SQL
Topic — aggregation
Aggregation problems for pushdown BI

Practice →

SQL
Topic — SQL · medium
Medium SQL for cloud-warehouse BI

Practice →


Cheat sheet — BI-tool decision recipes

  • 5-question decision tree. (1) Which cloud stack? Microsoft-shop → Power BI; Google → Looker; independent → any. (2) Where does the semantic layer live? dbt / MetricFlow → any; in-tool → Looker or Power BI; nowhere → Tableau / Sigma. (3) Who authors the metric? analyst → Tableau / Sigma; DAX engineer → Power BI; modeler in git → Looker. (4) Embedded analytics mission-critical? yes → Looker; side-feature → Power BI Embedded / Tableau Embedded; internal only → any. (5) TCO shape? per-viewer scaling → Tableau / Sigma; flat capacity → Power BI / Fabric; platform + per-editor → Looker.
  • When Tableau wins. Visualization-first culture, Salesforce integration critical, analyst community + huge existing skill pool, extract-based dashboards for high-viewership + stale-OK data, per-viewer cost model fits the org.
  • When Power BI wins. Microsoft / Azure / Fabric shop, DAX expertise, per-capacity flat pricing (10K+ viewers), Office 365 tight integration, OneLake as the platform-wide storage layer.
  • When Looker wins. dbt-backed or LookML-native metric governance, embedded customer-facing analytics, git-based governance is a hard requirement, modeler headcount available, metric drift is the top pain.
  • When Sigma wins. Snowflake / BigQuery / Databricks-first shop, spreadsheet-first analyst culture, input-table writeback is a real need (finance forecasts, ops overrides), no dedicated modeler headcount, dbt already handles canonical semantics.
  • Tableau extract recipe. Data Source → Extract → Edit; set incremental key to a monotonic BIGINT; schedule refresh hourly / nightly on Tableau Server; add a weekly full refresh to compact and re-stat.
  • Power BI DAX composite-model recipe. Create the aggregation table in the warehouse; add both fact and agg tables to the dataset; set fact to Direct Query, agg to Import; use the Manage Aggregations wizard to map agg columns to fact aggregates; test drill-through to fact.
  • Looker PDT recipe. Wrap the heavy SQL in a derived_table block; add sql_trigger_value: for change-based refresh or persist_for: for TTL-based; add indexes: / distribution_style: for warehouse hints; add a description: field so downstream users see the cache TTL.
  • Sigma input-table recipe. Add an input-table element with target warehouse.sigma_writeback.<name>; grant Sigma write permission to that schema; join the input table to a query element via LEFT JOIN on the key column; guard divisions with NULLIF.
  • Semantic-layer strategy across tools. Push canonical metrics to dbt / MetricFlow; expose via each tool's dbt Semantic Layer connector (Tableau, Power BI, Looker, Sigma all support this in 2026); use in-tool measures only for presentation-layer transforms (formatting, drill-through, per-page overrides).
  • Migration checklist (X → Y). (1) Inventory measures in tool X; (2) reconcile drifted definitions with business owners; (3) rebuild the semantic layer in tool Y or in dbt first; (4) port top-5 dashboards side-by-side and validate numbers; (5) run parallel for 90 days; (6) train analysts; (7) freeze new workbook creation in X; (8) retire tool X license.
  • Warehouse cost control (any BI tool). Push aggregate views into the warehouse (Snowflake dynamic table, BigQuery materialised view, dbt incremental model); repoint the BI tool at the aggregate; use warehouse result cache; monitor per-workbook cost with query tags.
  • RLS pattern (any tool). Prefer warehouse-native row-level security (Snowflake row access policies, BigQuery row-level security, Databricks Unity Catalog) over per-tool RLS — one policy, every BI tool inherits it.

Frequently asked questions

Is Power BI better than Tableau in 2026?

Neither is universally better — the answer depends on stack, team, and cost model. Power BI wins in Microsoft / Azure / Fabric shops (bundled with E5, tight OneLake and Excel integration, per-capacity pricing that scales cheaply to 10K+ viewers), for DAX-fluent teams, and where Copilot integration matters. Tableau wins in Salesforce-native shops, for visualization-first cultures where custom marks and dashboard aesthetics are the differentiator, and where the existing analyst pool is Tableau-trained (larger than any other BI tool). The tableau vs power bi question in 2026 almost always reduces to "which stack are you on?" — Microsoft shops pick Power BI, Salesforce shops pick Tableau, and independent Snowflake / BigQuery shops make a real trade-off based on team skill and the four axes in this article.

What is a semantic layer and where does it live in each BI tool?

A semantic layer is a shared, governed definition of business metrics (MRR, active user, churn) that every dashboard queries — the fix for the "three dashboards, three revenue numbers" bug. Tableau puts calc fields inside workbooks by default (analyst-friendly, drift-prone); published data sources give some governance. Power BI puts measures inside the shared dataset (semantic model) — every report bound to the dataset gets one MRR. Looker puts everything in LookML files under version control — every measure change is a pull request; drift is architecturally prevented. Sigma puts formulas in the workbook by default, with an optional Sigma Dataset for shared definitions. The 2026 shift: many teams push the semantic layer out of the BI tool entirely into dbt Semantic Layer / MetricFlow / Cube, then let every BI tool consume the same metrics — this is now a mature architecture, not a whiteboard idea.

Looker vs Tableau — when do I pick which?

Pick Looker when: (1) metric drift is a top-3 pain, (2) you have or can hire a modeler who owns LookML, (3) customer-facing embedded analytics is on the roadmap, (4) git-based governance of metrics is a hard requirement, (5) your warehouse is Snowflake / BigQuery / Databricks and pushdown-first fits your cost model. Pick Tableau when: (1) visualisation quality and custom marks matter more than metric governance, (2) analyst headcount is large and Tableau-trained, (3) Salesforce integration is a factor, (4) extract-based dashboards on Tableau Server are already load-tested, (5) per-Viewer pricing scales acceptably for your active-user count. The looker vs tableau decision in 2026 mostly hinges on whether you value code-first governance (Looker) or analyst-first iteration speed (Tableau) — both are correct in their context.

Sigma vs Looker for a Snowflake shop — what wins?

Depends on team shape and embedding needs. Sigma wins for analyst-heavy shops (200 analysts, 3 modelers) that already use dbt for canonical semantics and need spreadsheet-familiar UX + input-table writeback for finance / ops workflows. Looker wins for modeler-heavy shops (or shops that will hire modelers) that need customer-facing embedded analytics via the Embed SDK, want git-based governance of every metric, and value LookML's semantic-layer discipline over analyst iteration speed. The sigma vs looker decision in a Snowflake-native shop most often reduces to "do you have LookML modelers, and do you need embed?" — if yes to both, Looker; if no to both, Sigma; if mixed, model the 3-year TCO with real vendor quotes because per-viewer + warehouse compute (Sigma) versus platform + per-editor + free viewer (Looker) can swing five- to six-figure amounts per year depending on active-user shape.

Can I use dbt as the semantic layer instead of LookML?

Yes — this is now the recommended architecture for many 2026 data platforms. dbt Semantic Layer (built on MetricFlow) lets you define metrics in dbt YAML alongside your models — one MRR definition lives in metrics/mrr.yml and every downstream consumer (Tableau, Power BI, Looker, Sigma, custom apps) queries it via the dbt Semantic Layer API. The benefits: (1) the semantic layer version-controls with the transformations that feed it, (2) every BI tool sees the same numbers, (3) migrating BI tools no longer requires re-modeling metrics. The trade-offs: (1) the BI tool loses some of its native semantic-layer features (Looker LookML becomes redundant; Power BI datasets duplicate work), (2) query performance depends on how dbt Semantic Layer compiles metric SQL to your warehouse. In practice, most teams use dbt Semantic Layer for cross-tool canonical metrics and keep the BI tool's local semantic layer for presentation-layer transforms (formatting, drill-through, per-page overrides).

How much does migrating between BI tools cost in 2026?

Real cost is 3–9 months of senior data-engineer + analyst time plus the parallel-license bill during the migration. Direct license cost — the new tool's contract runs alongside the old for 60–120 days while you validate parity. Engineering cost — rebuilding the semantic layer (Looker LookML, Power BI dataset) or reconciling drifted metrics is the biggest line item; small shops can do it in 6 weeks, F500s take a full year for 100+ workbooks. Analyst cost — training curve is weeks for Tableau ↔ Power BI ↔ Sigma (drag-drop tools), months for anything → Looker (LookML curve). Business-continuity cost — the parallel-dashboards phase, where two versions of every KPI dashboard have to agree exactly, uncovers metric drift you didn't know you had. Budget conservatively: 6 months + 20–40% of one senior engineer + 20% of one analyst per 20 dashboards, plus 2 months of overlapping license spend. Teams that skip the semantic-layer reconciliation step (the reason most migrations happen) end up doing the migration twice.

Practice on PipeCode

Pipecode.ai is Leetcode for Data Engineering — every Tableau, Power BI, Looker, and Sigma recipe above ships with hands-on practice rooms where you wire the underlying SQL (aggregates, window functions, joins) against real graded inputs. PipeCode pairs every reading with 450+ DE-focused problems and a real-time scoring engine, so you never have to wonder whether your `tableau vs power bi` answer or your Looker LookML story holds up under a senior interviewer's depth probes.

Practice SQL now →
Aggregation drills →

Top comments (1)

Collapse
 
topstar_ai profile image
Luis Cruz

I found the comparison of the four BI tools to be quite insightful, particularly the discussion on the cost model per active user. The fact that Tableau's Hyper model and Power BI's Vertipaq engine have different performance characteristics is well-known, but the article highlights the importance of considering the semantic layer and governance model, as seen in Looker's LookML and git-based dev flow. I've worked with Tableau and Power BI in the past, and I can attest to the fact that the choice between them often comes down to the specific needs of the organization and the skillset of the team. Have you found that the choice of BI tool has a significant impact on the overall data engineering workflow, and if so, what are some key considerations for teams when making this decision?