guide
guide8 min read

Data Parity Checks: By Hand vs With an AI Agent

The manual migration workflow, what an agent takes over, and what still needs a human

A data parity check proves that a target dataset matches its source after a copy or migration, and an AI agent can run the comparison at row, aggregate and value level while a human keeps the sign-off. The cheapest fingerprint is a hash aggregate: Snowflake's HASH_AGG function returns a signed 64-bit value as NUMBER(19,0), and its docs note that two different tables can, though very unlikely, produce the same result. That caveat is why parity is a layered job, not a single query.

Key Takeaways

  • •Parity has three levels: row counts, aggregates, and value-level comparison. Passing a cheaper level does not prove the next one.
  • •A hash fingerprint is fast but not a proof. Snowflake's docs say two different tables can, though very unlikely, produce the same HASH_AGG result.
  • •By hand, the work is a pile of per-table SQL scripts, spreadsheet diffs and chat handoffs. It breaks on volume, type coercion and drift during the copy.
  • •An agent can run all three levels across every table and report mismatches with evidence. A human still approves tolerances, accepted differences and the cutover.
  • •Evaluate an agent on whether it compares values, explains its evidence, stays read-only by default and escalates instead of guessing.

The job and who does it today

Every copy or migration ends with the same question: is the target the same as the source? The copy might be a warehouse move, a pipeline rewrite, a replatform of a table to a new engine, or a backfill. Someone has to answer before consumers are pointed at the new location, because a silent difference becomes a wrong dashboard weeks later.

Today that someone is usually the analytics engineer or data engineer who wrote the migration. Sometimes a QA-minded teammate is borrowed for the cutover week. The work is rarely anyone's full-time job, so it is squeezed between the migration itself and the cutover date, and it is checked under time pressure.

The output the business actually needs is small: a yes or no per table, with a list of known differences and a reason for each. Getting there is the expensive part. The checks multiply by every table, every partition and every column, and each one has to be interpreted by someone who knows what the data means.

The manual workflow, step by step

The manual path usually starts with row counts. An engineer writes a count query against the source and the target, pastes both numbers into a sheet and compares them. It is quick, and it catches truncated loads and missing partitions. It does not catch duplicated rows offset by dropped ones, or any change inside a row.

Next come aggregates: sums of amounts, min and max of dates, distinct counts of keys, null counts per column. These are written by hand per table, so coverage follows whoever's patience ran out first. Teams on dbt often reach for the open-source dbt-audit-helper package, whose macros compare two relations or the values of a column between them. That helps, but someone still has to wire it up model by model and read each result.

Value-level comparison is the step most often skipped, because it is the most expensive. It means joining source to target on the key and comparing every column, or hashing rows and diffing the hashes. Cross-engine copies make it harder: timestamps change precision, decimals round differently, strings change collation and trailing whitespace, and each of those shows up as a false mismatch someone must triage.

Where the manual workflow breaks

The first failure is coverage. A migration of a few hundred tables does not get a few hundred carefully designed comparisons. It gets a rigorous check on the tables people worry about and a count check on the rest. The tables nobody worried about are where the surprises live.

The second failure is drift. The source keeps changing while the copy runs, so a comparison run on Monday is stale by Wednesday. Engineers end up re-running scripts by hand, comparing snapshots taken at different moments, and arguing about whether a difference is a bug or just lateness.

The third failure is the handoff. Findings live in a sheet, a chat thread and someone's terminal history. When a mismatch appears, the person who found it, the person who owns the table and the person approving cutover are often three different people. The evidence gets retold instead of shared, and the decision rests on a summary of a summary.

The fourth is false confidence. A hash match or a count match feels like proof. As the Snowflake documentation itself cautions, a match from a hash aggregate is strong evidence rather than a guarantee, so a team that stops at the cheap check has not finished the job.

What an agent does instead

The Quality Agent carries a skill, dw-data-parity-checks, described as comparing source and target datasets for row, aggregate and value-level parity after a copy or migration. That is the same three-level ladder an engineer climbs by hand, run across the whole set of tables instead of the ones someone had time for.

In practice you point it at a source and a target in a client you already use, since every agent is an MCP server and works from any MCP-compatible client such as Claude Code or Cursor. You ask something like: is the customer_id join safe after the migration? The agent compares the two datasets level by level and answers with the differences it found and the evidence behind each.

Two properties matter more than speed. First, the cheap levels gate the expensive ones, so a count mismatch is reported immediately instead of being buried under a value diff. Second, the answer is a list of specific rows, columns and aggregates that differ, not a score. An engineer can open one example and decide in a minute whether it is a real defect or an accepted difference.

The open-source edition runs locally and is read-only by default, which fits this job well: a parity check only needs to read both sides. Findings can chain to other agents, for example to the catalog for ownership of a mismatched table or to the schema agent when the difference traces to a changed column type.

Manual vs agent, side by side

StepBy handWith an agent
ScopeThe tables someone prioritizedEvery table in the migration set
Row countsOne query pair per table, pasted into a sheetRun automatically and compared in one pass
AggregatesHand-written sums, ranges and null countsGenerated per table from its column types
Value-level diffOften skipped because of costRun after cheaper levels pass or fail
Type and precision noiseTriaged mismatch by mismatchReported with the evidence, flagged for review
Re-runs during driftManual, easy to forgetRepeatable on request with the same checks
Evidence handoffSheet, chat and terminal historyOne report with examples for each difference
Cutover decisionHumanHuman

What still needs a human approval

Parity is not always equality. A migration may intentionally change a column type, drop a deprecated field, fix a known bad record or round a value. Only a person who owns the data can say a difference is acceptable. The agent surfaces it; the owner decides it.

Tolerances are the same kind of decision. Floating-point sums can differ in the last digits between engines, and a business may accept a tolerance on one metric and none on another. We recommend a human sets and records those thresholds before the first run, so the agent is not tuning its own pass criteria.

The cutover itself stays human. Repointing consumers, retiring the old table and closing the migration are actions with consequences. In the Pro edition, writes go through approval workflows with audit trails, and that is where we would put the gate. The agent's job is to make the evidence complete, not to make the call.

How to evaluate an agent for this job

Start with a migration you have already finished and know the answers to. Seed a few known defects: a dropped partition, a duplicated key, a rounded decimal, a timestamp that lost precision. Then see whether the agent finds each one, at which level, and with what evidence. An agent that only compares counts will miss half of them.

Check how it handles noise. A good agent distinguishes a real value difference from a representation difference and says which it believes, with an example. Ask it to explain a mismatch and verify that the explanation matches what you see when you query both sides yourself.

Finally, check the boundaries. Confirm it is read-only until you grant more, that it escalates ambiguous differences instead of resolving them, and that its report is something an approver can read without re-running anything. If you already run dbt data tests, keep them: they assert rules about one dataset, while parity compares two.

Frequently Asked Questions

What is the difference between a data parity check and a data quality test?

A data quality test asserts a rule about a single dataset, such as a key being unique or a column never being null. A parity check compares two datasets and asks whether they agree. Both matter after a migration: quality tests tell you the target is healthy, and parity tells you it is the same data as the source.

Is a matching row count or hash enough to prove parity?

No. Counts miss changes inside rows, and a hash match is strong but not absolute evidence. Snowflake's HASH_AGG documentation says two different tables can, though very unlikely, produce the same result. Use the cheap checks to find problems quickly, and compare values where the stakes justify it.

Does the agent change data in the source or target?

No, not for this job. The open-source edition is read-only by default, and a parity check only needs to read both sides. Any write, such as repointing consumers or retiring a table, belongs behind a human approval step.

Who should approve a parity result before cutover?

The owner of the data should approve accepted differences and tolerances, and whoever owns the migration should approve cutover. The agent supplies the evidence for both. We suggest recording each accepted difference with its reason, so the decision is auditable later.