SQL formatting and test data: preparing a database

Two jobs that happen before code touches a database: making a query readable and producing rows that look real. Both are mechanical, and both have a failure mode that looks like success.

Formatting is layout, not validation

A SQL formatter re-indents clauses and can normalise keyword case. Measured: select a,b from t where a=1 becomes SELECT a, / b / FROM t / WHERE a = 1. The tokens are the same; only whitespace and case changed. That means a formatted query can still be invalid, can still be slow, and can still be dangerous — the formatter does not parse semantics or check that a table exists.

Dialects are the other limit. Quoting rules, LIMIT versus TOP, and dialect-specific functions differ; a formatter that handles SELECT/JOIN/subqueries is not a SQL validator. Format for readability, then run the query against the real engine (or EXPLAIN) to learn whether it works.

Seeded test data

A seeded generator is deterministic: the same seed and the same columns produce the same rows. That is the property that makes fake data useful in tests — a failure on a generated name or date can be reproduced by keeping the seed, instead of photographing the output. The generator here also keeps identity fields consistent within a row: the email belongs to the name beside it, because columns are not independent when they describe one person.

FieldWhat it isWhat it is not
Name / emailInvented, repeatable from a seedA real person or a valid inbox
AddressA plausible-looking street and cityA postal address that validates
UUIDA random v4 identifierA durable key for production
DateAn ISO date in a fixed range“Today” or a business date

Formatting in a migration

A formatted query is easier to review, but the diff of a reformatted file can hide a semantic change in a wall of whitespace. Keep formatting commits separate from behaviour changes, or reviewers will scroll past the one line that matters. The same applies to generated SQL in an application: format it for logs and humans, but do not let a formatter rewrite a query whose quoting or dialect-specific syntax it does not fully understand.

Test data has a similar review problem. A fixture that is regenerated on every run produces a noisy diff; a fixture generated once from a seed is stable and reviewable. Commit the seed and the generator version, not the output alone, so the data can be reproduced when a test fails.

Generated data and privacy

Fake rows are not anonymised rows. A generated record has no relationship to a real person, which is what makes it safe for a demo and useless for privacy engineering: you cannot “swap the name” on a real record and call it anonymous, because the remaining fields can still identify someone. Generate into a test database, never into production, and keep the seed with the test so a failing case can be reproduced without storing the output.

Importing fixtures safely

CSV output quotes values that contain commas, quotes or newlines, so a standard importer reads it without a custom parser. Dates are ISO, which every importer reads unambiguously. The remaining risks are not formatting: a fixture loaded into a production database, a fake email that a test actually sends to, and a generated value that a uniqueness constraint rejects because the word lists are small. Keep the seed in the test, point the import at a test database, and do not treat generated rows as anonymised real data.

For a foreign key, a generator cannot invent a valid parent. Use a fixture factory that creates the parent first, then the child referencing it; the generator is for columns, not for referential integrity.

Questions this guide raises

Does formatting a SQL query make it safe?

No. It changes whitespace and keyword case, not semantics. A formatted query can still be invalid, slow, or a destructive statement. Use a parser or the database itself to validate, and never treat formatting as a review of what the query does.

Why does the same seed produce the same data?

The generator uses a seeded pseudorandom function rather than a fresh random source, so the sequence of values is fixed by the seed and the column list. That is what makes a test failure reproducible; it also means the values are not secret and will repeat across runs.

Can I use fake data to anonymise a real table?

No. Replacing some fields while keeping the structure can still leave a record identifiable, and the generated values have no relationship to the original. Anonymisation requires a defined threat model and usually more than substitution; keep fake rows in test environments and leave production data alone.