Stop Writing INSERT Statements One by One
Seed your database with hundreds of rows without losing your sanity
I needed 500 users in my database last week. Testing a pagination component. So I opened my SQL client and did this:
1
2
INSERT INTO users (name, email) VALUES ('Alice', 'alice@test.com');
INSERT INTO users (name, email) VALUES ('Bob', 'bob@test.com');
By row 12 I was done. Mentally. There’s a better way.
Just use SQL
Postgres can do the work for you. Cross join two lists of names, let random() handle the rest.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
INSERT INTO users (name, email, created_at)
SELECT
first_names.name || ' ' || last_names.name AS name,
LOWER(first_names.name || '.' || last_names.name || '@example.com') AS email,
NOW() - (random() * INTERVAL '365 days') AS created_at
FROM
(VALUES
('James'),('Mary'),('Robert'),('Patricia'),('John'),('Jennifer'),
('Michael'),('Linda'),('David'),('Elizabeth'),('William'),('Barbara'),
('Richard'),('Susan'),('Joseph'),('Jessica'),('Thomas'),('Sarah'),
('Christopher'),('Karen'),('Daniel'),('Lisa'),('Matthew'),('Nancy'),
('Anthony'),('Betty'),('Mark'),('Margaret'),('Donald'),('Sandra')
) AS first_names(name),
(VALUES
('Smith'),('Johnson'),('Williams'),('Brown'),('Jones'),
('Garcia'),('Miller'),('Davis'),('Rodriguez'),('Martinez'),
('Hernandez'),('Lopez'),('Gonzalez'),('Wilson'),('Anderson'),
('Thomas'),('Taylor'),('Moore'),('Jackson'),('Martin'),
('Lee'),('Perez'),('Thompson'),('White'),('Harris'),
('Sanchez'),('Clark'),('Ramirez'),('Lewis'),('Robinson')
) AS last_names(name)
ORDER BY random()
LIMIT 500;
Run that, then check what you got:
1
2
3
4
5
6
7
8
id | name | email | created_at
----+-------------------+-----------------------------+---------------------
1 | James Smith | james.smith@example.com | 2026-03-14 09:22:11
2 | Maria Johnson | maria.johnson@example.com | 2026-01-07 15:44:38
3 | Robert Williams | robert.williams@example.com | 2026-06-29 02:11:05
4 | Patricia Brown | patricia.brown@example.com | 2025-10-12 21:33:50
5 | John Garcia | john.garcia@example.com | 2026-05-18 11:07:22
...
30 first names x 30 last names = 900 combos, randomly sampled to 500. Actual names, emails that match, dates spread across the year. Took 12 milliseconds on my machine.
MySQL can do the same thing, just swap the VALUES lists for a recursive CTE. Same output.
Pull in Faker when names aren’t enough
Sometimes you need full profiles. Addresses, phone numbers, job titles. That’s when I reach for Faker.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from faker import Faker
fake = Faker()
users = []
for _ in range(5):
users.append({
'name': fake.name(),
'email': fake.email(),
'address': fake.address().replace('\n', ', '),
'job': fake.job(),
'created_at': fake.date_between(start_date='-1y', end_date='today'),
})
# One trip to the database
db.bulk_insert('users', users)
What you get back:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[
{
"name": "Dwayne Caldwell",
"email": "nunezmary@example.org",
"address": "25288 James Route, North Timothy, KS 54093",
"job": "Chartered legal executive",
"created_at": "2026-04-03"
},
{
"name": "John Smith",
"email": "kristy32@example.com",
"address": "5891 Daniel Gardens, Lake Joshua, MN 08783",
"job": "Solicitor",
"created_at": "2026-01-19"
},
...
]
The loop up top costs nothing. What kills you is 500 separate INSERTs hitting the database one at a time. That last line sends everything in one round trip.
Python: faker. Node: @faker-js/faker. PHP: fakerphp/faker. Go: go-faker. Ruby: faker. Same API everywhere.
Or just dump a CSV
I do this more than I’d admit. Write the data to a CSV, check it into git, import directly.
1
2
3
4
5
6
-- Postgres
\COPY users(name, email, city) FROM 'users.csv' CSV HEADER;
-- MySQL
LOAD DATA INFILE 'users.csv' INTO TABLE users
FIELDS TERMINATED BY ',' ENCLOSED BY '"' LINES TERMINATED BY '\n' IGNORE 1 ROWS;
1
COPY 500
Done. Teammate clones the repo, runs the same import, has the exact same data. No one installs Faker, no one runs a script. And six months later when I forget what the users table looks like, the CSV is sitting right there.
Anyway
Stop typing INSERTs manually. Pick raw SQL if you’re on Postgres, pick Faker if you need variety, pick CSV if you want something you can stash in git.
And use real looking names. test1, test2, asdf looks like garbage in a demo. You already sat down to write the seed script, might as well not make it ugly.