Get icon
Query icon
Queries icon
If icon
SlackIncomingWebhook icon
Set icon
Schedule icon

SQL Server Transactional MERGE Upsert with Watermark

Orchestrate an idempotent, transactional MERGE upsert into SQL Server with Kestra. Stage changed rows, MERGE with dedup, and advance a watermark atomically.

Categories
Data

An incremental load into SQL Server is rarely one statement. It is stage the delta, dedupe it, MERGE it into the target, then move the watermark forward, and if any one of those steps fails partway, the target table needs to end up exactly where it started. This blueprint runs the MERGE and the watermark update inside a single io.kestra.plugin.jdbc.sqlserver.Queries transaction, so a failure anywhere in that block rolls back completely instead of leaving a half-applied batch or a watermark that has drifted ahead of what actually committed.

How it works

  1. read_watermark (io.kestra.plugin.core.kv.Get) reads the inventory_merge_watermark key, falling through to the epoch on the first run.
  2. stage_new_rows (io.kestra.plugin.jdbc.sqlserver.Query) selects every row from dbo.inventory_updates changed since the watermark and stores it with fetchType: STORE.
  3. merge_and_advance_watermark (io.kestra.plugin.jdbc.sqlserver.Queries) runs a MERGE statement that updates matched rows and inserts new ones by sku and warehouse_id, then an afterSQL statement advances a watermark tracking table, both inside the same transaction (transaction defaults to true), so a failed MERGE rolls back the watermark update too.
  4. row_check (io.kestra.plugin.core.flow.If) flags the unusual case where rows were staged but the MERGE reported no output, which usually means a schema mismatch between the staging and target tables.
  5. advance_watermark (io.kestra.plugin.core.kv.Set) persists the new watermark to the KV Store only after the transactional MERGE has already committed downstream.
  6. notify confirms the row count and watermark; the errors block alerts separately on any failure.

What you get

  • A single-transaction MERGE and watermark update, so a partial failure never leaves the target half-updated.
  • Dedup on the merge key (sku, warehouse_id), collapsing late or duplicate rows instead of letting them accumulate downstream.
  • A watermark that only advances after the transaction commits, making every rerun idempotent.
  • A built-in sanity check that catches a silent schema mismatch between staging and target.

Who it's for

  • Data engineers running incremental loads into a SQL Server reporting or operational table.
  • Platform teams who need transactional guarantees a plain INSERT loop cannot provide.
  • Teams replacing a stored procedure and SQL Server Agent job with a version-controlled, retryable flow.

Why orchestrate this with Kestra

A stored procedure can run a MERGE, but coordinating the watermark update, the failure rollback, and the alerting all inside one transaction usually means hand-writing TRY/CATCH and XACT_ABORT logic in T-SQL, with no execution history outside the database itself. Kestra wraps the MERGE and the watermark advance in one Queries transaction, gives the whole run a versioned YAML definition and full execution history, and separates the success and failure notification paths declaratively instead of embedding them in the stored procedure.

Prerequisites

  • A SQL Server database with a source table (dbo.inventory_updates), a staging table (dbo.inventory_staging), a target table (dbo.inventory_current), and a watermark tracking table (dbo.merge_watermarks).
  • A Slack incoming webhook for notifications.

Secrets

  • SQLSERVER_URL: JDBC URL, for example jdbc:sqlserver://your-host:1433;trustServerCertificate=true.
  • SQLSERVER_USERNAME / SQLSERVER_PASSWORD: database credentials.
  • SLACK_WEBHOOK_URL: Slack incoming webhook URL.

Quick start

  1. Add the secrets above to your Kestra namespace.
  2. Confirm the source, staging, target, and watermark tables exist with matching columns.
  3. Run the flow once manually to seed the watermark, then enable the every_15_minutes schedule.
  4. Check the KV Store for the inventory_merge_watermark key after each run to confirm it advances.

How to extend

  • Replace the MERGE statement's match keys with your own natural or surrogate key.
  • Chain a downstream Snowflake or reporting refresh after merge_and_advance_watermark succeeds.
  • Add a row-count threshold to row_check to catch an unexpectedly large batch before it commits.
  • Swap the Schedule trigger for a Flow trigger so the merge fires the moment an upstream load finishes instead of on a fixed interval.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.