On this page
Validity benchmark
The realism benchmark asks whether generated data looks real. This one asks
something more basic: does it obey its own schema? A seed that breaks a CHECK
constraint, a composite key or a foreign key fails on INSERT, and test data
that cannot be loaded is not test data.
Five SQL schemas, written the way production databases declare their rules:
| Schema | What makes it hard |
|---|---|
saas_billing | UNIQUE emails, CHECK enums, CHECK (seats > 0 AND seats <= 500), CHECK (end_date > start_date) |
org_chart | a self-referencing manager_id that must form a tree, CHECK (salary BETWEEN ...) |
marketplace_m2m | a junction table with a composite PRIMARY KEY, UNIQUE (order_id, line_no), CHECK (price >= cost) |
hotel_bookings | CHECK (check_out > check_in), ranges on capacity and guests, a status enum |
retail_chain_6_levels | a six-level foreign-key chain from regions to payments |
Each schema is generated two ways:
misata_from_ddl:misata.from_ddl(ddl)thengenerate_from_schema, with the row counts set and nothing else configured.faker_script: the script people usually write instead. One Faker or NumPy call per column, foreign keys drawn uniformly from the parent ids, CHECK clauses not read.sdv_hma: SDV 1.38's multi-table model (HMA), used the way it is meant to be: fitted on a valid sample of the same schema (Misata's output under another seed, checked to have zero violations), told every primary and foreign key, and sampled at the same size. It is not given the CHECK clauses: SDV enforces those only when each one is re-declared as an SDV constraint, which is not what most users do. Optional; it runs only whensdvis installed.
The checker restates every rule in plain pandas and shares no code with Misata, so a pass is not Misata grading itself. It counts duplicate primary keys, orphaned foreign keys, NULLs in NOT NULL columns, duplicate UNIQUE values (single and composite), values outside CHECK enums and ranges, violated column-to-column CHECKs, and rows that sit on a cycle of a self-reference.
Results#
Misata 0.9.7, seed 7. Reproduce with
python benchmarks/validity_bench.py| Schema | Generator | Rows | pk duplicates | fk orphans | nulls | unique duplicates | enum violations | range violations | check violations | cycles | Total | Seconds |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| saas_billing | misata_from_ddl | 35,000 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1.25 |
| saas_billing | faker_script | 35,000 | 0 | 0 | 0 | 211 | 32,957 | 1,495 | 1,487 | 0 | 36,150 | 1.09 |
| saas_billing | sdv_hma | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | could not run: SynthesizerInputError: HMASynthesizer is not designed to handle a schema with more than 5 tables or relationship depth greater than 2. | 0 |
| org_chart | misata_from_ddl | 5,040 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.03 |
| org_chart | faker_script | 5,040 | 0 | 0 | 0 | 52 | 0 | 9,962 | 0 | 5,000 | 15,014 | 0.34 |
| org_chart | sdv_hma | 5,040 | 0 | 4,736 | 0 | 71 | 0 | 0 | 0 | 0 | 4,807 | 6.33 |
| marketplace_m2m | misata_from_ddl | 35,785 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.14 |
| marketplace_m2m | faker_script | 37,060 | 97 | 0 | 0 | 2,115 | 0 | 38,622 | 1,553 | 0 | 42,387 | 0.04 |
| marketplace_m2m | sdv_hma | 35,799 | 155 | 0 | 0 | 5,292 | 0 | 0 | 110 | 0 | 5,557 | 469.64 |
| hotel_bookings | misata_from_ddl | 32,300 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.17 |
| hotel_bookings | faker_script | 32,300 | 0 | 0 | 0 | 324 | 20,000 | 20,207 | 9,885 | 0 | 50,416 | 1.1 |
| hotel_bookings | sdv_hma | 32,300 | 0 | 0 | 0 | 404 | 0 | 0 | 2,651 | 0 | 3,055 | 660.02 |
| retail_chain_6_levels | misata_from_ddl | 45,836 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0.15 |
| retail_chain_6_levels | faker_script | 45,836 | 0 | 0 | 0 | 0 | 23,975 | 50 | 0 | 0 | 24,025 | 0.27 |
| retail_chain_6_levels | sdv_hma | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | n/a | could not run: SynthesizerInputError: HMASynthesizer is not designed to handle a schema with more than 5 tables or relationship depth greater than 2. | 0 |
- Misata from DDL
- Faker script
- SDV HMA
Show the numbers
| Schema | Misata from DDL | Faker script | SDV HMA |
|---|---|---|---|
| saas_billing | 0 | 36,150 | could not run |
| org_chart | 0 | 15,014 | 4,807 |
| marketplace_m2m | 0 | 42,387 | 5,557 |
| hotel_bookings | 0 | 50,416 | 3,055 |
| retail_chain_6_levels | 0 | 24,025 | could not run |
Source: benchmarks/results/summary.json, validity.totals; Misata 0.9.7, seed 7
Misata reports zero violations on every schema. SDV's multi-table model,
trained on valid data, still breaks rules it was never told: composite keys
(5,292 repeated (order_id, line_no) pairs), check_out > check_in (2,651
stays) and the manager hierarchy (4,736 manager_id values pointing at no
employee, because HMA does not model self-references). Its open-source
version refuses two of the five schemas outright (more than five tables, or
foreign keys more than two levels deep), and took 8 to 11 minutes to fit the
two it could, where Misata takes under two seconds.
Two things Misata's result costs, stated plainly:
- Composite keys can shorten a table. Where a composite key cannot be
made unique by renumbering (a junction table of two foreign keys), repeated
pairs are dropped, so
product_tagsdelivers 5,045 of the 6,000 rows asked for. A sequence column in a composite key (line_no) is renumbered within its group instead, and only rows past the CHECK maximum are dropped. - Durations come from the column names, not your data. A date pair
ordered by a CHECK is generated as the earlier date plus a duration that
fits the names: stays (
check_out > check_in) skew short with a median of three nights, shipping takes one to ten days, a contract'send_dateruns one month to two years. Other pairs repaired by the constraint move by a gap drawn from the rows that already satisfied it.
What fixing this found#
This benchmark was written for 0.9.7 and failed on its first run. The fixes it forced are in the release:
from_ddldropped every self-referencing foreign key, somanager_idwas random integers that looped back on themselves. It now keeps them, and the generator builds the hierarchy as a forest.- Table-level
CHECK (a > b)between two columns was ignored; it is now an inequality constraint. - Composite
PRIMARY KEY (a, b)andUNIQUE (a, b)were ignored, and once declared they were enforced per batch, which the union of batches broke. They now hold over the whole table. - An inequality repair set the violating value equal to the other column,
which breaks a strict
>and piles rows up at a zero gap. - UNIQUE text columns skipped semantic generation, so a UNIQUE
skuwas filled with sentences and aregions.namewith people's names.
Related#
- Seed a dev database from its DDL: the same guarantees on a schema you can run in a second
- SQL DDL to schema: which DDL rules Misata reads
- Realism benchmark: whether the data looks real, not only whether it is valid
- Benchmarks overview
Prefer generating data visually?
Misata Studio lets you turn natural language prompts into relational schemas with exact outcome invariants, live preview tables, and one-click database seeding without writing code.

