Free tool

Databricks data check SQL generator

Freshness, row count, null rate, duplicates and a job heartbeat, written for your database. Pick a dialect, name your table, copy the SQL. Runs in your browser, nothing you type is sent anywhere.

See the SQL

When each row was loaded or created.

Comma separated. Several columns check them as a combination.

Comma separated.

Output style
Advanced

Your checks

Freshness

Zero rows means healthy

Returns a row when the newest ingested_at is older than 30 hours.

SELECT newest
FROM (
  SELECT MAX(ingested_at) AS newest FROM main.analytics.events
) s
WHERE newest IS NULL
   OR newest < DATEADD(HOUR, -30, current_timestamp()) -- 30h threshold

Row count

Zero rows means healthy

Returns a row when the last 24 hours are more than 30% away from the same weekday over the last 4 weeks. Needs 28 days of history.

SELECT rows_24h, rows_prior_avg
FROM (
  SELECT
    COUNT(CASE WHEN ingested_at >= DATEADD(HOUR, -24, current_timestamp()) THEN 1 END) AS rows_24h,
    (
      COUNT(CASE WHEN ingested_at >= DATEADD(HOUR, -192, current_timestamp()) AND ingested_at < DATEADD(HOUR, -168, current_timestamp()) THEN 1 END) +
      COUNT(CASE WHEN ingested_at >= DATEADD(HOUR, -360, current_timestamp()) AND ingested_at < DATEADD(HOUR, -336, current_timestamp()) THEN 1 END) +
      COUNT(CASE WHEN ingested_at >= DATEADD(HOUR, -528, current_timestamp()) AND ingested_at < DATEADD(HOUR, -504, current_timestamp()) THEN 1 END) +
      COUNT(CASE WHEN ingested_at >= DATEADD(HOUR, -696, current_timestamp()) AND ingested_at < DATEADD(HOUR, -672, current_timestamp()) THEN 1 END)
    ) / 4.0 AS rows_prior_avg
  FROM main.analytics.events
  WHERE ingested_at >= DATEADD(HOUR, -696, current_timestamp())
) s
WHERE rows_prior_avg > 0
  AND ABS(rows_24h - rows_prior_avg) * 100 > rows_prior_avg * 30 -- 30% threshold

Null rate

Zero rows means healthy

Returns a row for each column whose null rate is above 1%.

  • Scans the whole table. Add a WHERE on the timestamp column to limit it to recent rows.
SELECT column_name, null_pct
FROM (
  SELECT 'user_id' AS column_name, 100.0 * (COUNT(*) - COUNT(user_id)) / NULLIF(COUNT(*), 0) AS null_pct FROM main.analytics.events
  UNION ALL
  SELECT 'event_type' AS column_name, 100.0 * (COUNT(*) - COUNT(event_type)) / NULLIF(COUNT(*), 0) AS null_pct FROM main.analytics.events
) s
WHERE null_pct > 1 -- 1% threshold

Duplicates

Zero rows means healthy

Returns each event_id value that appears more than once.

SELECT event_id, COUNT(*) AS occurrences
FROM main.analytics.events
GROUP BY event_id
HAVING COUNT(*) > 1

Heartbeat

Zero rows means healthy

Returns a row when no load has landed since the last expected run, so a job that never started still gets caught.

SELECT rows_in_window
FROM (
  SELECT COUNT(*) AS rows_in_window
  FROM main.analytics.events
  WHERE ingested_at >= DATEADD(HOUR, -24, current_timestamp()) -- 24h window
) s
WHERE rows_in_window = 0

How to run these

Each check is a plain SELECT. Save it to a file and run it from whatever already runs jobs for you.

Cron and psql

# hourly; mails you when the query returns any rows (grep . exits 0 only when it sees output)
0 * * * * psql "$DATABASE_URL" -At -f freshness.sql | grep . && echo "freshness check failed" | mail -s "data check" [email protected]

dbt singular test

Save the zero rows version as tests/orders_freshness.sql. dbt fails the test when the query returns any row.

Airflow

Use the One number style with SQLValueCheckOperator. The operator fails the task when the query returns no rows, so the zero rows style would fail a healthy check. The count always returns one row, andpass_value=0 passes only when it is 0.

SQLValueCheckOperator(
    task_id="orders_freshness",
    conn_id="warehouse",
    sql=FRESHNESS_SQL,
    pass_value=0,
)

arcodash monitor

Paste the One number version as a query, save it, and add a monitor on the failing column with the condition above 0. The monitor runs on a schedule and posts to Slack or email when it trips.

Databricks notes

Unity Catalog tables are addressed as catalog.schema.table. Names are case insensitive and quoted with backticks only when needed.

Time math uses DATEADD(HOUR, -30, current_timestamp()). Spark SQL timestamps are stored as UTC instants, so the comparison does not depend on the session zone.

Delta tables keep min and max statistics per file, so a filter on the timestamp column lets the row count and heartbeat checks skip most files.

Connecting Databricks to arcodash is covered on the Databricks connector page.

Questions

Is anything sent anywhere?

No. The SQL is generated by JavaScript in your browser and nothing you type leaves the page. The link you can copy keeps your settings in the part of the address after the #, which browsers never send to a server.

Why do the checks return zero rows when everything is fine?

Each check selects only the problems: a stale table, a count that moved too far, a column with too many nulls, a duplicated key. No problems means no rows, which is also how dbt tests and most scheduler checks read a result. If your tool needs a single value, switch to One number and the check returns a count of failing rows instead.

Why does the row count check need 28 days of history?

It compares the last 24 hours with the average of the same weekday over the previous 4 weeks, so a quiet Sunday is judged against other Sundays instead of a busy Tuesday. With less than 4 weeks of data the average is built from fewer days and the check is noisier.

What about time zones?

The checks compare your timestamp column with the database clock. If the column stores UTC and the session runs in another zone, the freshness threshold shifts by the offset. Each dialect page lists what to set so the two agree.

How do I get alerted when a check fails?

Run the SQL on a schedule and act on the result. A cron job, a dbt test or an Airflow task works. In arcodash, save the check as a query, wrap it as SELECT COUNT(*) AS failing FROM (<check>) t, and add a monitor that alerts Slack or email when failing is above 0.

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