glossary

What is a data validity check? Examples in SQL

A data validity check tests whether the values in a column are legal, not just present. Legal means inside an allowed set, such as status in active, archived or suspended; inside a range, such as age between 0 and 120; or in the right format, such as a two-letter country code. It catches typos, unit mix-ups and broken parsers. A null-rate check only asks whether a value exists, so a column can pass it and still be full of nonsense.

Present is not the same as right

Take a users.status column with zero nulls. A null-rate check is happy. But if a deploy starts writing actvie, every dashboard that filters on status = 'active' quietly loses those users. Nothing errors, nothing is missing, the numbers are just wrong.

A validity check encodes the rule for a column and counts the rows that break it. Three rule types cover most cases:

  • Allowed set. The value is one of a known list.
  • Range. The value sits between a minimum and a maximum.
  • Format. The value matches a pattern, such as an email or a country code.

One query, several rules

Count violations per rule in a single pass. The output also tells you which rule fired.

SELECT
  count(*) FILTER (WHERE age NOT BETWEEN 0 AND 120) AS bad_age,
  count(*) FILTER (WHERE status NOT IN ('active','archived','suspended')) AS bad_status,
  count(*) FILTER (WHERE email IS NOT NULL
                   AND email !~ '^[^@\s]+@[^@\s]+\.[^@\s]+$') AS bad_email,
  count(*) FILTER (WHERE country !~ '^[A-Z]{2}$') AS bad_country
FROM users;

Two details. NOT IN and NOT BETWEEN are never true for a NULL, so null values are skipped by those rules, which is what you want when nulls are covered by a separate check. And the email pattern is deliberately loose: it flags obvious garbage, not every address a mail server would reject.

Turn it into an alert

A monitor in arcodash reads one column of the first row, so alert on a total. Sum the rules into one number and fire above zero, or above a small tolerance if the source is messy.

SELECT bad
FROM (
  SELECT count(*) FILTER (
           WHERE age NOT BETWEEN 0 AND 120
              OR status NOT IN ('active','archived','suspended')
         ) AS bad
  FROM users
) t
WHERE bad > 0;

This form returns nothing when all is well, which suits any scheduler that alerts on a non-empty result. In arcodash, drop the final WHERE, monitor the bad column with > and 0, and schedule the query. The data observability monitors page covers the same pattern for null rate and duplicates.

Where validity checks go wrong

  • Scanning the whole table every run. Limit to recent rows with a created_at filter once the table is large, or the check becomes the slowest query you have.
  • Hard-coded lists that go stale. A new legitimate status will fire the alert until you update the list. That is the check working, but make updating it part of shipping the new value.
  • One giant rule. Keep a count per rule so the alert says which one broke.

Validity sits beside freshness and volume: arrived, enough, and sane.

Questions

How is a validity check different from a null-rate check?

A null-rate check counts missing values. A validity check counts present but wrong ones: negative ages, misspelled statuses, malformed emails. Run both, because a column can be fully populated and fully broken.

Should validity be enforced in the database instead?

Where you own the table, yes. CHECK constraints and enums reject bad rows at write time. Checks are for tables you do not control, such as loads from a vendor, a replica or a SaaS export.

How do I find which rule to write?

Write one for each column that has already broken, and for the few that feed a headline metric. Profile distinct values first to see what is actually in there.

get started

Trust the number. Try it on your data.

The demo is a live dashboard with its checks running. The trial is the full product for 14 days, no card.

querying in minutesunlimited seats