ChatGPT Built My Database Schema. Here's What It Got Wrong

AI-generated database schemas work... until they don't. Here's what ChatGPT and Claude get wrong about databases and how to fix it before you lose data.

You asked ChatGPT to design your database. It gave you a schema. You ran the migrations. Everything works!

Then you launch. 100 users sign up. 1,000 users. 10,000 users.

And your app slows to a crawl.

Pages that loaded in 100ms now take 5 seconds. Users complain. You check the database. It's on fire.

Here's what AI gets wrong about database design—and how to fix it.

Mistake #1: No Indexes on Foreign Keys

❌ What AI Generates

CREATE TABLE posts ( id INT PRIMARY KEY, user_id INT, title VARCHAR(255), content TEXT, created_at TIMESTAMP );

The problem: When you query SELECT * FROM posts WHERE user_id = 123, the database scans every single row. With 100,000 posts, this takes forever.

✅ The Fix: Add Indexes

CREATE TABLE posts ( id INT PRIMARY KEY, user_id INT, title VARCHAR(255), content TEXT, created_at TIMESTAMP, INDEX idx_user_id (user_id) );

Now lookups by user_id are instant. Queries that took 3 seconds now take 10ms.

Rule of thumb: If you're filtering or joining on a column, it needs an index.

Mistake #2: Missing Foreign Key Constraints

AI will create relationships between tables but forget to enforce them.

❌ What Happens

User deletes their account. But their 500 posts are still in the database, pointing to a user_id that doesn't exist.

Now when you try to display those posts with user info, your app crashes with "user not found."

✅ The Fix: Use Foreign Keys with Cascade

CREATE TABLE posts ( id INT PRIMARY KEY, user_id INT, title VARCHAR(255), content TEXT, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE );

ON DELETE CASCADE means when a user is deleted, all their posts get deleted too. No orphaned data.

Mistake #3: Wrong Data Types

ChatGPT loves using VARCHAR(255) for everything. But this causes problems:

  • Emails: Use VARCHAR(320) not VARCHAR(255). Email addresses can be up to 320 characters.
  • Phone numbers: Use VARCHAR(20) not INT. Phone numbers have + and - characters.
  • Prices: Use DECIMAL(10,2) not FLOAT. Floats cause rounding errors. $19.99 might become $19.990000001.
  • Status flags: Use ENUM('active', 'inactive', 'banned') not VARCHAR(50). This prevents typos and invalid values.

Mistake #4: No Unique Constraints

AI will let you create duplicate emails, duplicate usernames, duplicate order IDs.

❌ The Problem

Two users sign up with the same email. Now when one tries to log in, which account do you use?

Your app crashes, or worse—logs them into someone else's account.

✅ The Fix: Add UNIQUE Constraints

CREATE TABLE users ( id INT PRIMARY KEY, email VARCHAR(320) UNIQUE NOT NULL, username VARCHAR(50) UNIQUE NOT NULL, password_hash VARCHAR(255) NOT NULL );

Now the database prevents duplicates. If someone tries to sign up with an existing email, you get an error instead of corrupted data.

Mistake #5: N+1 Query Hell

This is the sneakiest performance killer.

Scenario: You want to display a list of 50 blog posts with author names.

AI-generated code does this:

// Get all posts posts = db.query("SELECT * FROM posts LIMIT 50") // For each post, get the author for post in posts: author = db.query("SELECT * FROM users WHERE id = ?", post.user_id) print(post.title, "by", author.name)

What just happened: 1 query to get posts + 50 queries to get authors = 51 database queries.

With 1,000 users online, that's 51,000 database queries. Your database melts.

✅ The Fix: Use Joins

// One query gets everything posts = db.query(` SELECT posts.*, users.name as author_name FROM posts JOIN users ON posts.user_id = users.id LIMIT 50 `) for post in posts: print(post.title, "by", post.author_name)

1 query instead of 51. Your database breathes easy.

Mistake #6: No Default Values or Constraints

AI forgets to set defaults. This causes bugs.

  • created_at: Should default to CURRENT_TIMESTAMP
  • is_active: Should default to TRUE
  • role: Should default to 'user'
CREATE TABLE users ( id INT PRIMARY KEY AUTO_INCREMENT, email VARCHAR(320) UNIQUE NOT NULL, is_active BOOLEAN DEFAULT TRUE, role ENUM('user', 'admin') DEFAULT 'user', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );

Mistake #7: Missing Timestamps

You'll want to know when things were created and updated. Always add these:

created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP

Why this matters:

  • Debug issues: "When did this bug start happening?"
  • User support: "Show me all orders from last week."
  • Analytics: "How many signups per day?"
  • Compliance: GDPR requires tracking when user data changes.

Check Your Database Schema

VibeCheck scans your schema for missing indexes, constraints, and performance issues. Get a free analysis in 60 seconds.

Scan My Database Free →

Quick Database Checklist

Before you launch, verify:

  1. Indexes: Every foreign key has an index
  2. Foreign keys: All relationships have constraints with ON DELETE CASCADE or SET NULL
  3. Unique constraints: Email, username, and other unique fields are marked UNIQUE
  4. Data types: Emails are VARCHAR(320), prices are DECIMAL, booleans are BOOLEAN
  5. Defaults: created_at, is_active, role all have sensible defaults
  6. NOT NULL: Required fields like email, password_hash are NOT NULL
  7. Timestamps: created_at and updated_at on every table

The Bottom Line

AI can generate a working database schema in seconds. But "working" doesn't mean "production-ready."

Add indexes. Add constraints. Choose the right data types. Use joins instead of loops.

Your database will thank you. Your users will thank you. And you won't wake up to a crashed app at 3 AM.