AI Prompt Engineering

Getting ChatGPT to Write Accurate Database Seeding Scripts Without Constraint Violations

July 15, 2026 9 min read

You paste your schema into ChatGPT, ask for a seed script, and get back a tidy block of INSERT statements β€” then watch them fail the moment you run them. Foreign key references point at rows that don't exist yet, primary keys collide, and a required column quietly gets a NULL. The script looked fine. The database disagrees.

The root cause isn't that ChatGPT can't write SQL. It's that it doesn't automatically reason about constraint dependencies unless you force it to. With the right prompting strategy, you can get output that inserts cleanly on the first run.

What You'll Learn

  • Why ChatGPT produces constraint-violating seed scripts by default
  • How to supply schema context in a format the model actually uses
  • Techniques for specifying correct insert order and ID management
  • How to surface unique and nullable constraints before they bite you
  • A full worked example with prompt and corrected output

Prerequisites

You should be comfortable writing basic SQL and understand what foreign keys, primary keys, and unique constraints are at a conceptual level. The examples use PostgreSQL syntax, but the prompting patterns apply to MySQL, SQLite, and SQL Server equally well.

Why ChatGPT Gets Seeding Wrong

ChatGPT generates seed data row by row, essentially treating each INSERT as an isolated unit. It doesn't build an internal dependency graph of your schema unless you explicitly ask it to. When you describe a table in passing β€” "I have users, orders, and order_items" β€” the model fills in plausible values without knowing your actual column names, constraint types, or referenced key values.

The result is seed data that looks structurally correct but fails at the database layer. The three most common failure modes are:

  • Wrong insert order: a child table is seeded before its parent table exists.
  • Invented foreign key values: the model picks an ID like 1 or 42 that doesn't exist in the parent table.
  • Ignored constraints: unique fields get duplicate values, and NOT NULL columns get omitted entirely.

Every fix flows from the same principle: give ChatGPT enough structural information that it can reason about constraints, rather than guess around them.

Give ChatGPT the Full Schema, Not Just the Table

The single highest-leverage change you can make is pasting your actual CREATE TABLE statements into the prompt. Not a prose description. Not "I have a users table with an id and email." The literal DDL.

Here are my CREATE TABLE statements. Generate a seed script that inserts 5 rows
into each table, respects all foreign key references, and never violates unique
or NOT NULL constraints.

[paste full DDL here]

When the model has the DDL, it can read the actual column names, data types, NOT NULL markers, and REFERENCES clauses. It no longer has to guess what "users" looks like. This alone eliminates the majority of null-constraint and column-mismatch errors.

If your DDL is large, trim it to the tables you're seeding. But never trim the FOREIGN KEY lines β€” those are the dependency map the model needs.

Specify Insert Order Explicitly

Even with the full DDL, ChatGPT sometimes produces inserts in the wrong order, especially when the table list is long or the relationships aren't immediately obvious. The safest fix is to tell the model the order directly.

Insert in this order: organizations, users, products, orders, order_items.
Each table's foreign keys will already be satisfied by the time you insert into it.

If you're not sure of the correct order yourself, ask ChatGPT to derive it first β€” as a separate step β€” before generating any INSERT statements:

Step 1: Given the DDL below, list the tables in safe insert order based on their
foreign key dependencies. Do not write any INSERT statements yet.

Step 2: Once I confirm the order, generate a seed script following it.

Breaking the task into two steps is worth the extra round-trip. It forces the model to reason about the dependency graph before it starts generating values, and gives you a checkpoint to catch ordering mistakes before they're baked into 50 insert rows.

This is a pattern that applies across many constraint-heavy code generation tasks β€” similar to how you'd approach prompting ChatGPT for accurate database transaction logic, where execution order is everything.

Handle Sequences and Auto-Increment IDs Correctly

One subtle but common failure: ChatGPT generates INSERT statements with explicit id values like 1, 2, 3. In PostgreSQL with a SERIAL or IDENTITY column, those explicit inserts succeed β€” but they don't advance the sequence. The next application-generated row tries to use id = 1 again and hits a duplicate key error.

There are two clean solutions. Ask ChatGPT to use DEFAULT for auto-increment columns and capture the generated IDs with RETURNING for use in child inserts:

INSERT INTO organizations (name, created_at)
VALUES ('Acme Corp', NOW())
RETURNING id;

Or ask ChatGPT to emit a SELECT setval() call after the inserts to reset the sequence to the max value used:

SELECT setval('users_id_seq', (SELECT MAX(id) FROM users));

Include this requirement in your prompt:

After inserting into any table with a SERIAL or IDENTITY column, add a setval()
call to reset the sequence to the highest inserted ID.

Unique and Composite Constraint Awareness

Unique constraints are invisible to ChatGPT unless you name them. The model will happily insert two users with email = 'test@example.com' if you don't tell it that email is unique. Composite unique constraints β€” like UNIQUE(user_id, product_id) on a wishlist table β€” are especially prone to duplication in generated seed data because the model treats the two columns independently.

In your prompt, list every unique constraint explicitly, even if it's already visible in the DDL:

Important constraints to respect:
- users.email is unique
- user_products(user_id, product_id) has a composite unique constraint
- orders.reference_code is unique

Make sure no two rows violate these constraints.

Repeating them outside the DDL isn't redundant β€” it signals to the model that these constraints are active concerns, not background noise.

Nullable vs. Required Fields: Closing the Gap

ChatGPT often omits optional columns entirely from INSERT statements when it's uncertain about their values. That's usually fine. But it also sometimes omits required columns when the DDL isn't in front of it β€” or when it misreads a default value as meaning the column is optional.

The fix is a two-part prompt instruction: tell ChatGPT to include all NOT NULL columns with realistic values, and tell it to leave truly nullable columns as NULL unless you specify otherwise.

Rules for column values:
- Every NOT NULL column must have a plausible, non-empty value.
- Columns that allow NULL should be set to NULL unless I specify a value.
- Do not invent values for columns you're unsure about β€” ask me instead.

That last line matters. ChatGPT's default behavior when uncertain is to fill in something plausible. Telling it to stop and ask redirects that uncertainty to you, where it belongs.

Seeding Across Relationships: A Worked Example

Here's a concrete prompt pattern for a schema with three related tables: teams, users, and projects, where users belong to teams and projects are owned by users.

I need a PostgreSQL seed script. Here is my schema:

CREATE TABLE teams (
  id SERIAL PRIMARY KEY,
  name VARCHAR(100) NOT NULL,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  team_id INT NOT NULL REFERENCES teams(id),
  email VARCHAR(255) NOT NULL UNIQUE,
  full_name VARCHAR(100) NOT NULL,
  role VARCHAR(20) NOT NULL CHECK (role IN ('admin', 'member')),
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE TABLE projects (
  id SERIAL PRIMARY KEY,
  owner_id INT NOT NULL REFERENCES users(id),
  title VARCHAR(200) NOT NULL,
  status VARCHAR(20) NOT NULL DEFAULT 'active',
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

Requirements:
1. Insert 3 teams, 6 users (2 per team), and 4 projects.
2. Insert in this order: teams β†’ users β†’ projects.
3. Each user's team_id must reference an ID from the teams insert.
4. Each project's owner_id must reference an ID from the users insert.
5. All emails must be unique.
6. User roles must only be 'admin' or 'member'.
7. After all inserts, reset each SERIAL sequence to the max inserted ID.
8. Use explicit IDs starting at 1 so foreign key references are predictable.

With that prompt, ChatGPT produces a script where every reference is grounded in an actual inserted row. You're not hoping the model guesses right β€” you've given it a deterministic map.

The technique of anchoring foreign key values to explicit IDs you control is the same discipline that applies when prompting for accurate index suggestions β€” you give the model the full structural picture so it reasons about your schema rather than a generic one.

Common Pitfalls When Using ChatGPT for Seed Data

Circular foreign keys

Some schemas have circular references β€” for example, users.manager_id references users.id. ChatGPT will either freeze on this or produce an insert that references a row that doesn't exist yet. The correct approach is to insert the self-referencing rows with manager_id = NULL first, then update them:

INSERT INTO users (id, email, manager_id) VALUES (1, 'ceo@example.com', NULL);
INSERT INTO users (id, email, manager_id) VALUES (2, 'manager@example.com', 1);
UPDATE users SET manager_id = 2 WHERE id = 1;

Tell ChatGPT: "If there are self-referencing foreign keys, insert the rows with NULL first, then UPDATE them after all rows exist."

CHECK constraint blindness

ChatGPT often ignores CHECK constraints when generating values. If you have CHECK (status IN ('draft', 'published', 'archived')), the model might still insert 'active'. List your CHECK constraint values in the prompt the same way you list unique constraints.

Timestamp ordering assumptions

If your schema has audit columns like created_at and updated_at, and child records logically postdate their parents, tell ChatGPT to use timestamps that reflect that ordering. An order created before the user who placed it is a subtle data quality problem that won't cause a constraint error but will confuse every query you run against the seed data.

Forgetting transaction wrapping

Ask ChatGPT to wrap the entire seed script in a transaction. If any insert fails, you want a clean rollback rather than a partially seeded database:

BEGIN;
-- all inserts here
COMMIT;

This is especially important in CI pipelines where seed failures need to leave the database in a known state. For more on how to handle risky schema changes safely, the patterns in writing accurate SQL migrations without locking production tables apply here too.

Missing junction table data

Many-to-many relationships sit in junction tables β€” user_roles, product_tags, etc. ChatGPT frequently forgets to seed these unless you name them explicitly. List every table you need seeded, including junction tables, in the prompt.

The same discipline around enumerating all affected components is why explicit prompting matters for related concerns like dependency injection configuration β€” missing one piece breaks the whole system.

Next Steps

You now have a reliable workflow for getting seed scripts that run cleanly. Here's what to do next:

  1. Dump your real DDL using pg_dump --schema-only or your ORM's schema export, and use that as the base context for every seeding prompt.
  2. Build a prompt template specific to your project β€” schema, constraint list, insert order, ID conventions β€” that you paste once and reuse.
  3. Add a two-step verification pass: after ChatGPT generates the script, ask it in a follow-up message to check its own output for constraint violations and list any it finds.
  4. Wrap seeds in transactions by default so partial failures don't corrupt your dev or test database state.
  5. Run seeds against a staging database first and pipe the output through your database's error log before committing them to version control.

Frequently Asked Questions

Why do ChatGPT-generated seed scripts fail with foreign key constraint errors?

ChatGPT inserts rows without verifying that referenced parent rows exist first. It doesn't automatically derive insert order from your schema unless you provide the full DDL and explicitly ask it to respect foreign key dependencies.

How do I tell ChatGPT to insert tables in the correct order for seeding?

Provide your full CREATE TABLE statements and ask ChatGPT to first list tables in safe insert order based on foreign key dependencies before writing any INSERT statements. Alternatively, specify the insert order yourself directly in the prompt.

How do I prevent duplicate key errors in ChatGPT-generated seed data?

List every unique constraint and composite unique constraint explicitly in your prompt, even if they appear in the DDL. ChatGPT is more likely to respect them when they are called out as active requirements rather than left as background schema detail.

Should I use explicit IDs or let the database auto-generate them in seed scripts?

Using explicit IDs makes foreign key references predictable and easier to verify. If you do use explicit IDs with SERIAL columns, always add a setval() call afterward to reset the sequence to the highest inserted value so application inserts don't collide.

How do I handle self-referencing foreign keys when seeding with ChatGPT?

Tell ChatGPT to insert self-referencing rows with NULL for the self-referencing column first, then issue UPDATE statements after all rows exist to fill in the references. This avoids the chicken-and-egg problem that causes constraint violations.

πŸ“€ Share this article

Sign in to save

Comments (0)

No comments yet. Be the first!

Leave a Comment

Sign in to comment with your profile.

πŸ“¬ Weekly Newsletter

Stay ahead of the curve

Get the best programming tutorials, data analytics tips, and tool reviews delivered to your inbox every week.

No spam. Unsubscribe anytime.