--- title: Generating sample data description: Fill tables with realistic, name-aware fake rows using seed — with foreign keys, reproducible runs, and per-column overrides. sidebar: order: 8 --- import Demo from '../../../components/Demo.astro'; Once a schema is in place you usually want rows to query against — enough of them to make `where`, `group by`, `order by`, and `limit` interesting. Typing those by hand is tedious. `seed` fills a table with **plausible, generated data** in one command, so you can get to querying straight away. It works in both [simple and advanced mode](/getting-started/modes/), with the same syntax. The examples use the [example library](/getting-started/example-library/). ## Filling a table `seed ` generates rows and inserts them. If you leave out the count, seed makes **20** rows: ```rdbms seed members 6 ``` ``` 6 row(s) seeded into members ┌───────────┬─────────────────┬────────────┐ │ member_id │ name │ joined │ ├───────────┼─────────────────┼────────────┤ │ 1 │ Bret Leffler │ 2023-10-16 │ │ 2 │ Santa Nicolas │ 2024-03-14 │ │ 3 │ Vivienne Barton │ 2024-03-04 │ │ 4 │ Fatima Rippin │ 2022-11-14 │ │ 5 │ Lola Cole │ 2025-05-29 │ │ 6 │ Reina Waters │ 2023-11-25 │ └───────────┴─────────────────┴────────────┘ ``` The data is **random**, so your rows will differ — run it again for a fresh set, or see [Reproducible runs](#reproducible-runs) to pin it. `member_id` was filled automatically (it is a `serial` column), exactly as it is for an [`insert`](/reference/inserting-and-editing-data/); seed leaves `serial` and `shortid` columns to the database. Notice the values are not random noise: `name` produced believable people and `joined` produced recent dates. That is because seed reads each **column's name** to decide what to generate. ## How columns are filled A column's **name** chooses a generator, but only when the column's [type](/reference/types/) fits — a column called `email` typed `int` will not get an address. Matching is case-insensitive and looks at the name's parts (`first_name`, `signup_date`, `is_active`). A representative set: | Column name looks like… | You get | For types | |---|---|---| | `name`, `first_name`, `last_name`, `full_name` | a person's name | `text` | | `email` | an email address | `text` | | `username`, `login`, `handle` | a username | `text` | | `phone`, `mobile`, `tel` | a phone number | `text` | | `city`, `country`, `state`, `street`, `zip` | address parts | `text` | | `company`, `employer` · `job`, `position` | a company / job title | `text` | | `description`, `bio`, `notes`, `comment` | a sentence or paragraph | `text` | | `url`, `website` · `color` | a URL / hex colour | `text` | | `price`, `amount`, `cost`, `salary` | a money amount | `int`, `real`, `decimal` | | `age` · `quantity`, `qty`, `stock` | a plausible age / small number | `int` | | `date`, `dob`, `created_at`, `updated_at` | a recent (or birth-window) date | `date`, `datetime` | | `year`, `*_year`, `published`, `founded`, `birth_year` | a plausible year (a birth window for `birth_year`) | `int` | | `priority`, `severity`, `rating`, `stars` | a value from a built-in set (low/medium/high, 1–5, …) | `text`, `int` | | `is_active`, `has_*`, `enabled` | `true` / `false` | `bool` | When a column's name **isn't** recognised, seed falls back to its **type**: placeholder words for `text`, a number for `int` and `real`, a recent value for `date`. A column like `isbn` or `title` — text with no specific meaning seed can infer — gets placeholder words; pin it with the [`set` clause](#choosing-values-yourself) if you want something specific. Two name families are handled specially: - **Identifier-like names** that are *not* a foreign key or the primary key — `code`, `sku`, `ref`, `barcode`, a `*_id` that isn't a relationship — get **unique** values, so they read like real identifiers and never collide. - **Open-ended choice names** — `status`, `role`, `type`, `category`, and the like — have no single sensible default, so seed fills them with placeholder text and then [tells you](#columns-seed-cant-guess) to choose the real values yourself. (Common choices that *do* have a conventional set — `priority`, `severity`, `rating` — are filled from it, per the table above.) Any column with a `unique` [constraint](/reference/constraints/) always gets collision-free values, whatever its name — that is a correctness guarantee, not a guess. ## Foreign keys Seed respects [relationships](/reference/relationships/). A foreign-key column is filled by **sampling from the rows that already exist** in the parent table, so every generated reference is valid. Seed the parent first: ```rdbms seed authors 5 seed books 6 ``` ``` 6 row(s) seeded into books ┌─────────┬──────────────────┬───────────┬───────────┬─────────────────────────┐ │ book_id │ title │ author_id │ published │ isbn │ ├─────────┼──────────────────┼───────────┼───────────┼─────────────────────────┤ │ 1 │ Austen Wuckert │ 4 │ 1976 │ sit nihil │ │ 2 │ Leanne Fisher │ 3 │ 1961 │ in ex │ │ 3 │ Ludwig Bahringer │ 5 │ 1986 │ sapiente provident │ │ 4 │ Jeff Little │ 3 │ 2021 │ fugit sint eum │ │ 5 │ Kameron Moore │ 1 │ 1995 │ incidunt cumque quia │ │ 6 │ Walker Hammes │ 5 │ 1997 │ enim saepe consequuntur │ └─────────┴──────────────────┴───────────┴───────────┴─────────────────────────┘ ``` Every `author_id` points at a real author (1–5). Duplicates are expected and correct — one author has many books. `published` got a plausible year on its own (seed recognises year-like columns); `title` and `isbn` are placeholder text, since neither name maps to a real-world generator — pin them with [`set`](#choosing-values-yourself) if you want something specific. If a parent table is **empty**, seed refuses rather than inventing a reference that would break the relationship: ``` cannot seed `books`: parent table `authors` (referenced by `author_id`) has no rows. Seed or insert into `authors` first. ``` This mirrors the order you would insert data by hand, and quietly teaches foreign-key dependency order. A junction table linking two parents (a many-to-many bridge) is filled with **distinct combinations** of the parents' keys; if you ask for more rows than there are combinations, seed makes as many as it can and tells you. ## Reproducible runs Add `--seed ` to make a run **repeatable**: the same number produces the same data, so a teacher can hand out one dataset and a demo stays stable. ```rdbms seed members 6 --seed 42 ``` ``` 6 row(s) seeded into members ┌───────────┬─────────────────┬────────────┐ │ member_id │ name │ joined │ ├───────────┼─────────────────┼────────────┤ │ 1 │ Bret Leffler │ 2023-10-16 │ │ 2 │ Santa Nicolas │ 2024-03-14 │ │ 3 │ Vivienne Barton │ 2024-03-04 │ │ 4 │ Fatima Rippin │ 2022-11-14 │ │ 5 │ Lola Cole │ 2025-05-29 │ │ 6 │ Reina Waters │ 2023-11-25 │ └───────────┴─────────────────┴────────────┘ ``` Run that again and you get the very same six members. "The same data" is relative to the table's current contents: because foreign keys and unique values read the rows already present, reproducibility assumes the same starting point. ## Choosing values yourself Seed's guesses are a starting point. The optional `set` clause pins how one or more columns are filled. It reuses syntax you already know from [`where`](/reference/querying-and-inspecting/) and `update`, so there is nothing new to learn — four forms: | Form | Example | Meaning | |---|---|---| | Fixed value | `set status = 'active'` | every row gets the same value | | Pick from a list | `set role in ('admin', 'editor', 'viewer')` | a random choice from the list | | Named generator | `set contact as email` | force a specific generator | | Range | `set price between 10 and 100` | a value in the range (also dates) | `status` has no built-in set — its real values are domain-specific — so it is the natural column to pin: ```rdbms seed tickets 6 set status in ('open', 'pending', 'closed') ``` ``` 6 row(s) seeded into tickets ┌───────────┬──────────────────────────┬─────────┬──────────┐ │ ticket_id │ subject │ status │ priority │ ├───────────┼──────────────────────────┼─────────┼──────────┤ │ 7 │ atque libero │ pending │ high │ │ 8 │ culpa maiores et │ open │ low │ │ 9 │ natus rerum animi │ open │ medium │ │ 10 │ sapiente rem │ closed │ low │ │ 11 │ placeat blanditiis quasi │ closed │ high │ │ 12 │ sed exercitationem │ closed │ low │ └───────────┴──────────────────────────┴─────────┴──────────┘ ``` `status` takes your values; `priority` filled itself from its built-in set (low/medium/high). Comma-separate several `set` clauses to pin more than one column at once. Text values and list items are **quoted** (`'admin'`), exactly as elsewhere; only numbers are bare. Dates in a range are quoted too (`set joined between '2023-01-01' and '2024-12-31'`). A range on a number column takes numeric bounds, a range on a date column takes date bounds — a mismatched bound is a friendly error. The named generators you can use after `as` are: `age`, `bool`, `city`, `color`, `company`, `country`, `date`, `datetime`, `email`, `first_name`, `job`, `last_name`, `name`, `paragraph`, `password`, `phone`, `price`, `product`, `sentence`, `state`, `street`, `url`, `username`, `zip`. :::note If you pin a `unique` column (or a single-column primary key) to a fixed value or a list that is too short to fill every row, seed stops and explains — it cannot make 20 distinct rows from three choices. Use a generator or a longer list. ::: ## Filling one column `seed
.` fills **one column across the rows that already exist**, rather than adding new rows — the natural follow-up to [`add column`](/reference/columns/), and the way to repair a single column seed guessed wrongly. Combined with `set`, it sets that column deliberately: ```rdbms seed tickets.status set status in ('open', 'closed') ``` ``` 12 row(s) seeded into tickets ┌───────────┬──────────────────────────┬────────┬──────────┐ │ ticket_id │ subject │ status │ priority │ ├───────────┼──────────────────────────┼────────┼──────────┤ │ 1 │ ad natus │ closed │ low │ │ 2 │ iusto officia │ closed │ high │ │ 3 │ possimus error │ closed │ high │ │ 4 │ reprehenderit et earum │ open │ low │ │ 5 │ cumque autem voluptas │ open │ low │ │ 6 │ maxime sed sit │ closed │ medium │ │ 7 │ atque libero │ open │ high │ │ 8 │ culpa maiores et │ closed │ low │ │ 9 │ natus rerum animi │ closed │ medium │ │ 10 │ sapiente rem │ closed │ low │ │ 11 │ placeat blanditiis quasi │ closed │ high │ │ 12 │ sed exercitationem │ open │ low │ └───────────┴──────────────────────────┴────────┴──────────┘ ``` Only `status` changed; the other columns are untouched. Column-fill **refuses** primary-key and autogenerated (`serial` / `shortid`) columns — you do not "fill in" an identity column — and on an empty table it is a no-op. ## Columns seed can't guess Open-ended choice columns — `status`, `role`, `type`, and the like — get placeholder text, because there is no single sensible value for them. After a seed, the playground points this out: ```rdbms seed tickets 6 ``` ``` 6 row(s) seeded into tickets ┌───────────┬────────────────────────┬────────────────────────────────────┬──────────┐ │ ticket_id │ subject │ status │ priority │ ├───────────┼────────────────────────┼────────────────────────────────────┼──────────┤ │ 1 │ ad natus │ temporibus eos rerum │ low │ │ 2 │ iusto officia │ iure aut provident │ high │ │ 3 │ possimus error │ consequatur consequuntur molestiae │ high │ │ 4 │ reprehenderit et earum │ recusandae est quibusdam │ low │ │ 5 │ cumque autem voluptas │ ea praesentium pariatur │ low │ │ 6 │ maxime sed sit │ sapiente et et │ medium │ └───────────┴────────────────────────┴────────────────────────────────────┴──────────┘ ``` > `status` filled with generic text — they look like fixed value sets. Pin > them next time with `set status in ('…', '…')`, or fix these rows with > `seed tickets.status set status in ('…', '…')`. Here `priority` was filled from its built-in set automatically, so only `status` is flagged. The two fixes it suggests are the [`set` clause](#choosing-values-yourself) on the next seed, and [column-fill](#filling-one-column) to repair the rows you just made. If a `check` constraint restricts a column to a list of values (`check status in ('open', 'closed')`), seed reads that list and uses it automatically — no override needed. ## Limits - The most you can seed at once is **10,000** rows; more is a friendly error (a guard against a typo like `seed members 1000000`). Seed in smaller batches if you genuinely need more. - `seed members 0` does nothing. A whole `seed` is a **single step** in the history: one [`undo`](/using-the-playground/undo-and-history/) removes every row it added, not one row at a time. ## Syntax ```rdbms-syntax seed
[] [set = | in (, ...) | as | between and ][, ...] [--seed ] seed
. [set ...] [--seed ] ``` See also [Inserting & editing data](/reference/inserting-and-editing-data/), [Relationships](/reference/relationships/), [Columns](/reference/columns/), and [Constraints](/reference/constraints/).