---
title: "Incremental model"
description: "An incremental model is a transformation that processes only new or changed source data on each run and merges it into an existing table, instead of rebuilding the whole table from scratch every time."
canonical: "https://motherduck.com/glossary/incremental-model/"
related:
  - title: "Data loading patterns | MotherDuck Docs"
    url: "https://motherduck.com/docs/key-tasks/loading-data-into-motherduck/loading-patterns/"
  - title: "Microbatch: how to supercharge dbt-duckdb with the right incremental model"
    url: "https://motherduck.com/blog/microbatch-dbt-duckdb/"
  - title: "Agentic Data Engineering: Build a Data Stack with AI Agents | MotherDuck"
    url: "https://motherduck.com/videos/agentic-data-engineering-stack/"
---

# Incremental model

> An incremental model is a transformation that processes only new or changed source data on each run and merges it into an existing table, instead of rebuilding the whole table from scratch every time.

## Overview

As a table grows, rebuilding it completely on every pipeline run gets slower and more expensive—reprocessing years of historical orders every time a new day of data arrives wastes compute on rows that haven't changed. An incremental model solves this by processing only the new or changed rows since the last run and merging them into the existing table, so run time stays roughly proportional to the size of the new data rather than the size of the whole table.

The term is most associated with dbt, where `materialized='incremental'` is one of the standard materialization strategies (alongside `table`, `view`, and `ephemeral`), but the underlying pattern—process new data, merge into existing state—predates dbt and shows up in any hand-rolled pipeline that avoids full reprocessing.

## How dbt incremental models work

A dbt incremental model uses the `is_incremental()` macro to add a filter that only applies on incremental runs (not on the first run, when the table doesn't exist yet):

```sql
{{
  config(
    materialized='incremental',
    unique_key='order_id'
  )
}}

SELECT
    order_id,
    customer_id,
    order_date,
    amount
FROM {{ source('raw', 'orders') }}

{% if is_incremental() %}
WHERE order_date > (SELECT max(order_date) FROM {{ this }})
{% endif %}
```

On the first run, the whole source table is selected and the target table is created. On every subsequent run, only rows newer than the current maximum `order_date` in the target are selected, and dbt merges them in according to the configured strategy.

## Incremental strategies

Different adapters support different merge strategies, configured via `incremental_strategy`:

- **append**: just insert the new rows (fastest, but can create duplicates if a row is reprocessed).
- **merge**: upsert new rows by `unique_key`, updating existing rows and inserting new ones.
- **delete+insert**: delete rows matching the new batch's keys, then insert the new batch (a common strategy where native `MERGE` isn't available).

dbt-duckdb, the DuckDB adapter for dbt, supports the standard incremental materialization; because DuckDB is fast and has no cluster warm-up cost, incremental models on DuckDB are often chosen for query-time efficiency and to avoid re-scanning large Parquet/S3 datasets on every run, rather than purely for cost reasons the way they might be on a metered cloud warehouse.