---
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.

<Demo src="/casts/seed.cast" title="One line fills a table with realistic, ready-to-query rows — seed reads each column's name to decide what to make." />

The examples use the [example library](/getting-started/example-library/).

## Filling a table

`seed <table> <count>` 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 <n>` 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 <table>.<column>` 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 `not null` column seed cannot produce a value for — the only real case is
  a `not null blob` — makes seed refuse the whole command and name the column,
  rather than fail partway through.

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 <Table> [<count>] [set <col> = <value> | in (<value>, ...) | as <generator> | between <low> and <high>][, ...] [--seed <n>]
seed <Table>.<column> [set ...] [--seed <n>]
```

See also [Inserting & editing data](/reference/inserting-and-editing-data/),
[Relationships](/reference/relationships/), [Columns](/reference/columns/), and
[Constraints](/reference/constraints/).
