Incremental SQL Model
Build an incremental append model with cursor tracking
This example shows a SQL model that incrementally appends new events using cursor tracking.
1. Source Data
-- models/raw/events.sql
-- @kind: table
SELECT * FROM (VALUES
(1, 'u1', 'click', '/pricing', '{}', '2026-03-28 10:00:00'),
(2, 'u2', 'view', '/features', '{}', '2026-03-28 11:00:00'),
(3, 'u1', 'purchase', '/checkout', '{}', '2026-03-29 09:00:00'),
(4, 'u3', 'signup', '/register', '{}', '2026-03-29 14:00:00'),
(5, 'u2', 'view', '/docs', '{}', '2026-03-30 08:00:00')
) AS t(event_id, user_id, event_type, page_url, metadata, event_time)
2. Incremental Model
-- models/staging/stg_events.sql
-- @kind: append
-- @incremental: event_time
-- @constraint: event_id NOT NULL
-- @constraint: event_type IN ('click', 'view', 'purchase', 'signup')
-- @audit: row_count > 0
SELECT
event_id,
user_id,
event_type,
page_url,
metadata,
event_time
FROM raw.events
ORDER BY event_time
3. Run
ondatrasql run
Running 2 models...
[OK] raw.events (table, backfill, 5 rows, 187ms — first run)
[OK] staging.stg_events (append, backfill, 5 rows, 352ms — first run)
4. Run Again
ondatrasql run
Running 2 models...
[OK] raw.events (table, skip, 0 rows, 0s — no dependencies)
[OK] staging.stg_events (append, incremental, 0 rows, 259ms — unchanged)
Nothing changed, nothing ran.
How It Works
- First run (backfill): All data is loaded into the target table
- After first run: OndatraSQL records
MAX(event_time)from the target table - Subsequent runs: Smart CDC automatically detects changes in
raw.eventsusing DuckLake time-travel and only appends new rows - No manual filtering: You write a plain SELECT — OndatraSQL handles incremental logic via CDC
Ondatra Labs