PowerUser Plugin for dbt

Profile Every CTE in a dbt Model to Find the Join That Inflates Your Rows

Debug a dbt model in one click. Profile CTEs shows the runtime and row count of every CTE, so the join that inflates your rows stands out.

Profile every CTE in a dbt model to find the join that inflates your rows
TL;DR
  • What it is. One-click buttons in the PowerUser Plugin for dbt. Five sit above every dbt model. Execute CTE and Profile CTEs sit above each CTE.
  • Who it is for. Analytics engineers handed a ticket on a model they did not write, or a dashboard that broke after a change.
  • What you get. The runtime and row count of every CTE in one click, so a join that multiplies rows stands out from the rest.

A dashboard shows duplicate rows and inflated totals after a recent change. The model compiles and the tests pass. To debug a dbt model like this by hand, you pull each CTE out of the Jinja and run it alone, in order. Then you compare row counts yourself. On a long model, that takes hours.

Run One CTE Without Pulling It Out of the Jinja

Click Execute CTE above any CTE. The extension compiles the Jinja, runs the CTEs that one depends on, and shows the output of that CTE alone. You check a change to one CTE without running the whole model. After a fix, run that CTE again and compare its row count with the number you expected. The CTE preview guide covers the same flow from the docs.

The orders.sql editor with the model code lens Execute Query, Explain, Optimize, Document and Review above line 1, and the CTE code lens Execute CTE: orders and Profile CTEs above the first CTE.

Profile Every CTE to Spot the One Out of Line

Click Profile CTEs. The extension runs every CTE one by one and writes its runtime and row count beside it. In the demo, orders took 4.4 seconds for 99 rows. The payments CTE took 644 ms for 113 rows, and order_payments took 181 ms for 99 rows.

Profile CTEs results in orders.sql. The orders CTE reads 4.4s and 99 rows, payments reads 644ms and 113 rows, and order_payments reads 181ms and 99 rows.

Now suppose someone changed a left join in this model to a full outer join. In the presenter's example, one CTE jumps from about 99 rows to about 990 and runs for about 10 seconds. Every other CTE in the model stays near 100 rows. That one CTE holds the bad join, and you found it in a single click. As a rule, compare each CTE with the CTE before it. A CTE that returns more rows than its inputs, right after a join, is the first place to look.

Spotlight 4 video, debug dbt models and profile CTEs in one click

Explain or Optimize the Model Without Writing a Prompt

The model row holds four more buttons. Each one opens an Altimate Code session with the prompt already written:

  • Explain walks through the model step by step, Jinja variables included, and flags anything worth a second look.
  • Optimize returns ways to make the model faster or cheaper.
  • Document writes the model docs in place.
  • Review checks the code changes you made.

The Explain: orders.sql and Optimize: orders.sql chat tabs. The explanation lists the final select, the resulting output columns and a section titled Why use Jinja loops here.

The Explain guide lists every way to start an explanation.

Install the dbt VS Code extension

Frequently Asked Questions

Install the dbt VS Code Extension. Free to Start.

Power User for dbt is an open source VS Code extension that adds column lineage, autocomplete, AI docs, query previews and health checks to your dbt project.

The Altimate platform: team spend by warehouse, dbt column lineage and an Altimate Code session recap