Alex Verhoevenbackend & data · amsterdam

· data quality · sql

The cheapest data quality check is a row count

Before any framework, before anything with a YAML file: count the source, count the destination, compare, and keep the history.

Every data quality tool I've evaluated wants a config file, a scheduler, and a taxonomy of expectations. They're fine. But the check that has caught the most real incidents for me, across four jobs, is a table with five columns and a nightly job that fills it.

CREATE TABLE ops.row_counts (
  checked_at  timestamptz NOT NULL DEFAULT now(),
  table_name  text        NOT NULL,
  day         date        NOT NULL,
  src_rows    bigint,
  dst_rows    bigint,
  PRIMARY KEY (table_name, day, checked_at)
);

Once a night, for each table that matters, count rows per day in the source system and in the warehouse for the last seven days (not just yesterday — late data changes yesterday's answer), and insert a row. Then two alerts:

The weekday-matched baseline matters. Sunday is not Tuesday, and a check that doesn't know that will page you every Sunday until someone turns it off, which is the usual fate of alerts.

What it catches

A broken pipeline (zero). A load that ran twice (double). A partial load after a timeout (0.6×). A source that quietly stopped emitting (flat). An upstream change that started filtering rows nobody told you about (a step down that stays down). That's most of the incident list from any year I can remember.

What it doesn't

Wrong values in the right number of rows. Nulls where there weren't nulls. A column that changed meaning. That's what the expensive tools are for, and they earn their keep there. This is for the other eighty percent, and it costs one table, one cron entry, and an afternoon.