What it checks
Whether a source table and its target hold the same number of rows, one segment at a time. Here a segment is one source customer. The check is the same as in Import File Example: Reconciliation by Segment.
The file version lists every customer by hand, fifteen rows for five table pairs and three customers. This script lists the five table pairs once and reads the customers from the data. When a fourth customer starts sending files, the next materialization adds five child tests for it, one per table pair, and nobody edits anything.
This is the pattern Exercise 11 teaches: a CROSS JOIN between a list of objects and a list of values taken from the data.
Part of Template Test Examples: Metadata Script.
The template
| Setting | Value |
|---|---|
| Name | Metadata Script Example - Reconciliation by Segment |
| Level | Table |
| Generation source | Metadata Script |
| Script data source | The template's own data source (SAMPLE_DW) |
| Everything else | Identical to the import file version: source and target row counts filtered on {{var.segment_column}} = '{{var.segment_value}}', numeric tolerance {{var.tolerance}}, and the same variables |
The metadata script
metadata-script-example-reconciliation-by-segment.sql:
-- One child test per table pair and per source customer. The customer list is read from the
-- data, so a new customer gets its own children at the next materialization without anyone
-- editing a list.
WITH pairs AS (
SELECT column1 AS source_table, column2 AS target_table
FROM VALUES
('ACCOUNT', 'DIM_ACCOUNT'),
('POLICY', 'DIM_POLICY'),
('CLAIM', 'DIM_CLAIM'),
('PAYMENT', 'FACT_PAYMENT'),
('TRANSACTION', 'FACT_TRANSACTION')
),
customers AS (
SELECT DISTINCT SOURCE_CUSTOMER AS segment_value
FROM RAW.ACCOUNT
WHERE SOURCE_CUSTOMER IS NOT NULL
)
SELECT 'RAW' AS "schema.name",
p.source_table AS "table.name",
p.target_table || ': SOURCE_CUSTOMER = ' || c.segment_value AS "test.key",
'STAR_SCHEMA' AS "var.target_schema",
p.target_table AS "var.target_table",
'SOURCE_CUSTOMER' AS "var.segment_column",
c.segment_value AS "var.segment_value",
'0' AS "var.tolerance"
FROM pairs p
CROSS JOIN customers c
ORDER BY "table.name", "test.key"
How it works:
pairsis the only list a person maintains: which raw table loads which star schema table.customersreads the distinctSOURCE_CUSTOMERvalues fromRAW.ACCOUNT, the customer master. Nulls are filtered out, because a null would produce a child test that counts nothing.CROSS JOINpairs every table with every customer: five pairs times three customers, fifteen rows.test.keyis the target table plus the segment, for exampleDIM_CLAIM: SOURCE_CUSTOMER = CUSTOMER_1002. One source table appears three times, each with its own key.var.toleranceis the text'0'on every row. Return a per-pair value frompairsif some tables are allowed to differ.
What it returns
Fifteen rows, the same as the import file. The three rows for RAW.CLAIM:
table.name |
test.key |
var.target_table |
var.segment_value |
|---|---|---|---|
| CLAIM | DIM_CLAIM: SOURCE_CUSTOMER = CUSTOMER_1001 | DIM_CLAIM | CUSTOMER_1001 |
| CLAIM | DIM_CLAIM: SOURCE_CUSTOMER = CUSTOMER_1002 | DIM_CLAIM | CUSTOMER_1002 |
| CLAIM | DIM_CLAIM: SOURCE_CUSTOMER = CUSTOMER_2001 | DIM_CLAIM | CUSTOMER_2001 |
Results on SAMPLE_DW
Verified 2026-10-19: 15 children, 12 pass, 3 fail, the same as the import file version.
| Pair | CUSTOMER_1001 | CUSTOMER_1002 | CUSTOMER_2001 |
|---|---|---|---|
| CLAIM -> DIM_CLAIM | Fail: 21,036 vs 20,644 | Fail: 22,576 vs 22,154 | Fail: 10,727 vs 10,515 |
| The other four pairs | Pass | Pass | Pass |
DIM_CLAIM is short for every customer, by about 2% each time, which points at the claim load rather than at one customer's feed.
Compared with the import file
| Import file | Metadata script | |
|---|---|---|
| The table pairs | Repeated on every row | Listed once |
| The customers | Typed on every row | Read from RAW.ACCOUNT at each materialization |
| A new customer | No child tests until someone adds five rows | Five child tests at the next materialization |
| A customer that stops sending data | Its rows stay until removed | Its child tests remain while it is still in RAW.ACCOUNT |
| Child tests | 15 | The same 15, with the same keys |
Adapting it
Segment by something else. Replace SOURCE_CUSTOMER in customers and in var.segment_column with your segment column, such as a region, a line of business, or a load date.
Take the segments from each source table. This script reads one customer list for every pair. If each table has its own set of values, build customers as a UNION of SELECT DISTINCT ... FROM each source, tagged with the source table, and join on it instead of crossing.
Limit the segments. Add a filter to customers, for example only the customers active in the last 30 days, so that dormant segments do not produce child tests.
Pitfall: an empty segment list deletes everything. If RAW.ACCOUNT is empty when a job materializes the template (mid-reload, for example), the script returns no rows. When Delete tests is selected, every child test of the template is then deleted. Materialize from a job only after the source has loaded, or clear Delete tests on that job step.
Pitfall: segment values in keys. A key is stored in lower case. Two segment values that differ only in letter case collide, and Preview & Validate reports Duplicate test key.