Skip to content
SQLCraft
  • 100% client-side isolated
  • no upload
  • types inferred from values

Let the data write the schema

Paste JSON, NDJSON or a CSV export and get a CREATE TABLE with types inferred from the values, sized VARCHARs, honest NULL handling and batched INSERT statements — written for the dialect you pick.

Examples

Your data

JSON array, a single object, { data: […] }, one object per line, or CSV.

JSON · 2 rows
Nothing is uploaded: the parse happens in this tab.

Generated script

Paste it straight into a client.

CREATE TABLE imported_data (
  id INTEGER NOT NULL PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
  email VARCHAR(32) NOT NULL,
  full_name VARCHAR(16) NOT NULL,
  monthly_spend NUMERIC(12,2) NOT NULL,
  active BOOLEAN NOT NULL,
  signed_up_at TIMESTAMPTZ NOT NULL,
  plan VARCHAR(8) NOT NULL,
  address JSONB NOT NULL,
  referrer VARCHAR(8)
);

INSERT INTO imported_data (id, email, full_name, monthly_spend, active, signed_up_at, plan, address, referrer)
VALUES
  (1, 'ana.alvarez@northlabs.com', 'Ana Alvarez', 249.5, TRUE, '2024-03-11T09:24:00Z', 'growth', '{"city":"Madrid","country":"Spain"}', NULL),
  (2, 'marc.bishop@bluetraders.com', 'Marc Bishop', 79, FALSE, '2023-11-02T18:05:41Z', 'starter', '{"city":"Dublin","country":"Ireland"}', 'partner');

Inferred schema

Read from the values, then spelled for the target dialect.

Columns
9
Rows
2
Statements
2
Notes
3
ColumnTypeInferred fromNULLLongest
idINTEGERintegerno1
emailVARCHAR(32)stringno27
full_nameVARCHAR(16)stringno11
monthly_spendNUMERIC(12,2)floatno5
activeBOOLEANbooleanno5
signed_up_atTIMESTAMPTZtimestampno20
planVARCHAR(8)stringno7
addressJSONBjsonno37
referrerVARCHAR(8)stringyes7
INSERT style
CREATE TABLETypes inferred from the values
INSERT statementsNULL is written as NULL, never an empty string
DROP TABLE IF EXISTS firstHandy for a scratch database
Add an identity id columnThe dialect's own auto-increment idiom

What the inference decided

Every substitution, rename and width is reported rather than assumed.

  • The existing `id` column became the generated primary key.
  • 1 INSERT statement(s), 100 rows per statement.
  • 2 rows × 9 columns read as JSON.

How the types were chosen

The rules that turn a value into a column definition.

Strings
VARCHAR sized to the longest value, rounded to 8
Long text
Past 8000 characters, the dialect's text type
Numbers
Whole numbers are integers, decimals are floats
Booleans
true / false / yes / no, never widened into a number
Dates
YYYY-MM-DD stays a date; a time widens to a timestamp
UUID
The 8-4-4-4-12 pattern, mapped per dialect
Objects
A nested object or array becomes a JSON column
Empty
A column that is always empty becomes text

JSON and NDJSON keep their types, CSV arrives as text9 keys read2 rows aligned

Types come from the values

Every value in a column is classified and the classes are merged, so the column type is what the data actually is rather than what a generator guessed. Whole numbers become integers, decimals become floats, a date that meets a timestamp widens to a timestamp, and an always-empty column becomes text.

Sizes are measured, not invented

A VARCHAR is sized from the longest value in that column rounded up to a multiple of eight, so the definition is reproducible for the same input. Past 8000 characters a bounded VARCHAR is the wrong tool, so the column becomes the dialect's long text type and the decision is reported.

The script is ready to run

The DROP, CREATE TABLE and INSERT statements are written for the dialect you choose, quoting identifiers the way that engine expects, writing NULL as NULL rather than an empty string, and naming every substitution so nothing changes behind your back.

  • SQL Formatter

    Pretty-print a query with clause phrases on their own line, commas broken only inside a list and subqueries on their own indent — or collapse it back to one line. Keyword casing, indent width, leading or trailing commas and a line-length limit are all switches, and the token stream is compared before and after so a reformat can never rewrite the query.

    Open tool
  • Dialect Converter

    Move one query between six engines and get the spellings that actually differ: identifier quoting, string escaping, boolean literals, type names, auto-increment columns and the LIMIT / TOP / FETCH FIRST family. What cannot be translated — ILIKE, JSONB operators, QUALIFY, ON CONFLICT — is listed as a gap instead of being silently dropped.

    Open tool
  • Mock Data

    Pick a preset table — users, orders, products or subscriptions — set a row count and a seed, and get rows that hold together: an email built from the same row's names, a status from a small vocabulary, dates that never read the clock. Four presets, twenty-three column kinds and one identical result per seed.

    Open tool

Schema builder FAQ

How the types are inferred, where VARCHAR lengths come from, how exotic keys are handled, and how the INSERTs are batched.

How are the column types decided?

Every value in a column is classified, and the classes are merged: two kinds that differ widen to text, except where a real rule applies. A boolean never widens into a number — a yes/no column becomes text rather than a 0/1 column that changes its meaning. A date that meets a timestamp becomes a timestamp, because every engine here stores a date in a timestamp column but not the reverse.

Where does the VARCHAR length come from?

From the longest value in that column, rounded up to a multiple of eight with a floor of eight characters. Past 8000 characters a bounded VARCHAR is the wrong tool, so the column becomes the dialect's long text type (TEXT, NVARCHAR(MAX), CLOB) and that decision is reported. Same input, same length, every time.

What happens to keys that are not valid identifiers?

They are sanitised: spaces and punctuation collapse into underscores, a leading digit is prefixed, and a key that collides with another is given a numeric suffix. The rename is listed so nothing changes silently. The same sanitising is applied to the table name you supply.

Are the INSERT statements batched?

You choose. One statement per row is easier to debug, because a single bad row can be skipped. A multi-row statement with many VALUES tuples is what most engines load fastest, with the batch size under your control up to 1000 rows per statement. NULL is written as NULL, never as an empty string.

Does it detect the CSV delimiter?

Yes. Comma, semicolon and tab are all recognised from the header line, quotes are honoured including doubled quotes to escape one, and a short row is padded rather than dropped. A file with a header but a single column is rejected with an explanation instead of producing a table with no useful columns.