TSV Schema Generator
Point it at a TSV and get the schema you were about to write by hand. Every column is scanned for what its values actually are — integer, decimal, boolean, date, or text — and how long the longest one is, then emitted as a CREATE TABLE for your dialect, a JSON Schema, a TypeScript interface, or a plain table you can read. A column with any empty cell is marked nullable; one with none gets NOT NULL.
How to use
- Paste or drop the file. The first row must be the header — it supplies the column names.
- Pick an output format and, for the SQL dialects, a table name. Names are lower-cased and non-alphanumerics become underscores so the identifier is always valid.
- Read the plain report first if you're unsure about a column — it lists the inferred type, nullability, and maximum length for every column in one view.
- Copy the DDL and review it before running it. Inference is a first draft, not an authority.
How types are inferred
A column gets a type only when every non-empty value in it fits: all values matching -?\d+ makes it an integer, all parsing as numbers makes it decimal, all matching an ISO-ish date makes it a timestamp, all being true/false/yes/no/1/0 makes it boolean, and anything else is text. One stray N/A in a numeric column therefore turns the whole column into text — which is the correct, conservative answer, and a useful signal that the column needs cleaning before load. Find the offender with value counts, or fix it with fill empty cells.
The gap worth knowing about: a column of 0 and 1 is ambiguous — it satisfies both integer and boolean, and integer wins. Likewise a column of long digit strings (phone numbers, zip codes, order references) is reported as an integer even though storing it as one would strip leading zeros and, past 19 digits, lose precision. If a numeric-looking column is really an identifier, override the type to text by hand. Nothing can infer intent from digits.
Per-format notes
PostgreSQL uses bigint / numeric / boolean / timestamp, and either varchar(n) rounded up to a sensible bucket or text when values exceed 255 characters. In Postgres there is no performance reason to prefer varchar(n) over text — the length is documentation and a constraint, nothing more. MySQL uses DECIMAL(18,6) for decimals rather than DOUBLE, on the grounds that a file of money amounts is more common than a file of physics measurements. SQLite gets its four storage classes; the declared types are advisory there anyway.
JSON Schema is emitted as a 2020-12 schema for an array of row objects, with nullable columns typed as a union with null and non-nullable ones listed under required. additionalProperties: false is set, so it validates the exact shape. TypeScript marks nullable columns optional with ? and quotes any key that isn't a valid identifier.
FAQ
Does it scan the whole file or a sample?
Every row, always. Sampling is what makes other schema inferrers wrong — the one bad value is usually not in the first thousand rows. The cost is that very large files take a moment.
Where are the primary keys and indexes?
Not generated — a unique-looking column isn't necessarily a key, and guessing wrong produces DDL that fails on load or, worse, succeeds and enforces the wrong thing. Add the key yourself; value counts on a candidate column tells you quickly whether it's actually unique.
How do I load the data after creating the table?
Use the database's bulk loader, not INSERT statements: \copy my_table FROM 'f.tsv' WITH (FORMAT csv, DELIMITER E'\t', HEADER) in Postgres, LOAD DATA INFILE in MySQL, .mode tabs then .import in SQLite. If you'd rather have INSERTs — for a small file, or to paste into a migration — TSV → SQL generates them.
Can it generate an Avro or Parquet schema?
Not currently. The four formats here cover the cases people ask for; if you need Avro, the plain report has everything you need to write it — the type and nullability per column are the only inputs.
Privacy
100% client-side. No upload. See the privacy policy.