Datatrail
Blog / Fundamentals 10 min read

What Is Data Lineage? A Plain-English Guide for Data Teams

Last updated June 2026 · Datatrail

Lineage map
Lineage mapped from query history. Read-only connection.
0

Read-only connection. Datatrail never moves or mutates your data.

Data lineage is the map of how data moves through your warehouse, from the raw tables a system loads to the models, metrics, and dashboards built on top of them. It records the relationships between assets so you can answer two questions instantly: where did this number come from, and what breaks if I change this table? Good lineage turns a pile of disconnected SQL into a graph you can actually read, and it is the foundation for debugging, change management, and trust in your data.

The core idea: data has parents and children

Every table or view in a warehouse is built from something. A staging model like stg_stripe_charges reads from a raw loaded table such as stripe.charges. A mart like fct_orders joins several staging models together. A dashboard tile reads from fct_orders. Lineage captures those parent-to-child relationships as a directed graph: each asset is a node, and each dependency is an edge pointing from source to consumer.

Once you have that graph, a lot of hard questions become easy. If stripe.charges stops loading at 6am, lineage tells you that stg_stripe_charges, fct_orders, and the three dashboards downstream are all at risk. If an analyst asks "is this revenue figure trustworthy?", lineage lets you walk back from the dashboard tile to the exact source column it was derived from.

Table-level vs column-level lineage

There are two resolutions of lineage, and the difference matters a lot in practice.

Table-level lineage tracks dependencies between tables: fct_orders depends on stg_orders and stg_customers. This is enough to understand pipeline order and rough blast radius. It is also relatively cheap to build, because you can often infer it from the FROM and JOIN clauses of each model.

Column-level lineage goes deeper and tracks how individual fields flow. It tells you that fct_orders.revenue is computed from stg_stripe_charges.amount divided by 100, and that dim_users.email traces all the way back to raw.users.email_address. This precision is what lets you answer "if I rename customers.email, which exact metrics and dashboards reference it?" without guessing. Most teams start with table-level and graduate to column-level lineage once they need to reason about specific fields rather than whole tables.

Table-levelColumn-level
Which tables feed this tableWhich columns feed this column
Good for pipeline order and rough impactGood for precise change impact and trace-to-source
Cheaper to computeRequires parsing SQL select lists

Why lineage matters

Lineage is not documentation for its own sake. It pays off in concrete, recurring situations.

  • Debugging. When a dashboard shows a wrong or stale number, lineage lets you walk upstream node by node until you find the broken source or the model that introduced the error, instead of grepping through dozens of SQL files.
  • Impact analysis before a change. Before you drop a column or refactor a model, lineage shows the downstream blast radius: every model, exposure, and dashboard that will feel the change. That is the difference between a planned migration and a 9am fire.
  • Onboarding and trust. A new engineer can read the graph and understand how the warehouse is wired in an afternoon. Analysts can verify that a metric comes from the source they expect.
  • Deprecation. Lineage shows what is actually unused. If a staging model has no downstream consumers, it is a safe candidate to retire.

How lineage is built

Lineage can be assembled from several signals, and the strongest systems combine them.

From dbt metadata. If you use dbt, the manifest.json already encodes model-to-model dependencies through ref() and source() calls. That gives you accurate table-level lineage inside the dbt project for free. The catch is the dbt boundary: dbt knows nothing about the raw loads upstream of your sources, or about the BI tools downstream of your marts. Lineage that stops at the dbt boundary misses both ends of the story.

From the warehouse query log. Every warehouse keeps a history of executed SQL. Snowflake exposes QUERY_HISTORY and the column-resolved ACCESS_HISTORY view; BigQuery has INFORMATION_SCHEMA.JOBS and audit logs; Redshift and Postgres expose their own statement logs. By parsing these statements you can reconstruct lineage that reflects what actually ran, including ad-hoc queries and pipelines outside dbt. Column-level lineage in particular is derived by parsing the select lists, joins, and expressions in those logged statements.

From static SQL parsing. You can also parse model SQL directly without running it, resolving FROM, JOIN, CTEs, and select expressions into a dependency graph. This is how column-level edges get drawn even for models that have not run recently.

A connection to read this metadata should be read-only by design. Lineage is built by reading query history and schema metadata, never by writing to or mutating your tables. Datatrail connects to your warehouse read-only and reconstructs the graph from Snowflake access history, dbt metadata, and parsed SQL, so the lineage reflects how your data actually flows rather than a diagram someone drew last quarter.

Trace-to-source in practice

The single most useful thing lineage gives you is trace-to-source. Pick any field a stakeholder cares about, say orders.revenue on an executive dashboard, and walk it backward:

  1. The dashboard tile reads fct_orders.revenue.
  2. fct_orders.revenue is summed from stg_stripe_charges.amount_usd.
  3. stg_stripe_charges.amount_usd is raw.stripe.charges.amount cast and divided by 100.
  4. raw.stripe.charges is loaded by the Stripe connector every hour.

Now you know the exact transformation chain, the freshness expectation at the source, and every other asset that shares that chain. If revenue looks wrong, you have a short list of suspects instead of the whole warehouse.

Lineage and observability go together

Lineage is most powerful when paired with monitoring. Knowing the graph is good; knowing that raw.stripe.charges is six hours late and which dashboards that late load will poison is better. That is why lineage sits at the center of broader freshness and quality monitoring: an alert without lineage tells you something broke, while an alert with lineage tells you what will break downstream and who to notify.

If you want to see your own warehouse as a navigable graph, with table and column-level edges traced from real query history rather than a hand-drawn picture, you can explore how Datatrail builds lineage from a read-only connection. It is a live product you can point at your Snowflake, BigQuery, Redshift, or Postgres warehouse today and start tracing any column back to its source.

See how your data flows, end to end

Connect your warehouse read-only and map lineage, freshness, and downstream impact before a change breaks a dashboard. Transparent pricing, no card to start.