Database Design Guide 2026

šŸ“… May 17, 2026ā€¢ā±ļø 20 min readā€¢šŸ·ļø Database, Backend, SQL

Learn database design principles, normalization, indexing, relationships, and when to use SQL vs NoSQL. Build efficient, scalable databases.

šŸ—„ļø SQL vs NoSQL

SQL (Relational)

Structured data, ACID transactions

  • • PostgreSQL, MySQL, SQL Server
  • • Fixed schema
  • • Strong consistency
  • • Complex queries (JOINs)
  • • Vertical scaling

NoSQL

Flexible data, horizontal scaling

  • • MongoDB, Redis, Cassandra
  • • Flexible schema
  • • Eventual consistency
  • • Simple queries
  • • Horizontal scaling

šŸ“ Database Normalization

1NF (First Normal Form)

• Atomic values (no arrays)
• Each column has unique name
• No repeating groups

2NF (Second Normal Form)

• Must be in 1NF
• No partial dependencies
• All non-key attributes depend on entire primary key

3NF (Third Normal Form)

• Must be in 2NF
• No transitive dependencies
• Non-key attributes depend only on primary key

šŸ”— Relationships

-- One-to-Many (User has many Posts)
CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  name VARCHAR(100),
  email VARCHAR(100) UNIQUE
);

CREATE TABLE posts (
  id SERIAL PRIMARY KEY,
  user_id INTEGER REFERENCES users(id),
  title VARCHAR(200),
  content TEXT
);

-- Many-to-Many (Students and Courses)
CREATE TABLE students (
  id SERIAL PRIMARY KEY,
  name VARCHAR(100)
);

CREATE TABLE courses (
  id SERIAL PRIMARY KEY,
  name VARCHAR(100)
);

CREATE TABLE enrollments (
  student_id INTEGER REFERENCES students(id),
  course_id INTEGER REFERENCES courses(id),
  enrolled_at TIMESTAMP DEFAULT NOW(),
  PRIMARY KEY (student_id, course_id)
);

-- One-to-One (User and Profile)
CREATE TABLE profiles (
  id SERIAL PRIMARY KEY,
  user_id INTEGER UNIQUE REFERENCES users(id),
  bio TEXT,
  avatar_url VARCHAR(255)
);

⚔ Indexing

-- Create Index
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_posts_user_id ON posts(user_id);

-- Composite Index
CREATE INDEX idx_posts_user_date ON posts(user_id, created_at);

-- Unique Index
CREATE UNIQUE INDEX idx_users_email_unique ON users(email);

-- When to Index:
āœ… Foreign keys
āœ… Columns in WHERE clauses
āœ… Columns in ORDER BY
āœ… Columns in JOIN conditions

āŒ Small tables
āŒ Columns with low cardinality
āŒ Frequently updated columns

šŸŽÆ Query Optimization

-- āŒ Bad: SELECT *
SELECT * FROM users;

-- āœ… Good: Select only needed columns
SELECT id, name, email FROM users;

-- āŒ Bad: N+1 Query Problem
for (const user of users) {
  const posts = await db.query('SELECT * FROM posts WHERE user_id = ?', [user.id])
}

-- āœ… Good: JOIN or IN clause
SELECT users.*, posts.* 
FROM users 
LEFT JOIN posts ON users.id = posts.user_id;

-- Use EXPLAIN to analyze queries
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'test@example.com';

-- Pagination
SELECT * FROM posts 
ORDER BY created_at DESC 
LIMIT 20 OFFSET 0;

-- Better: Cursor-based pagination
SELECT * FROM posts 
WHERE id < last_seen_id 
ORDER BY id DESC 
LIMIT 20;

šŸ” Security

  • Parameterized Queries: Prevent SQL injection
  • Least Privilege: Minimal database permissions
  • Encryption: Encrypt sensitive data at rest
  • Backup: Regular automated backups
  • Audit Logs: Track database changes
  • Connection Pooling: Limit connections

šŸ“Š MongoDB Example

// Schema Design
const userSchema = new Schema({
  name: String,
  email: { type: String, unique: true, index: true },
  posts: [{
    title: String,
    content: String,
    createdAt: { type: Date, default: Date.now }
  }]
})

// Embedded vs Referenced
// Embedded (1-to-few, data accessed together)
{
  _id: ObjectId("..."),
  name: "John",
  address: {
    street: "123 Main St",
    city: "NYC"
  }
}

// Referenced (1-to-many, independent access)
// User
{ _id: ObjectId("user1"), name: "John" }

// Posts
{ _id: ObjectId("post1"), userId: ObjectId("user1"), title: "..." }
{ _id: ObjectId("post2"), userId: ObjectId("user1"), title: "..." }

// Indexes
db.users.createIndex({ email: 1 })
db.posts.createIndex({ userId: 1, createdAt: -1 })

šŸ“‹ Design Checklist

  • ☐ Choose SQL vs NoSQL based on requirements
  • ☐ Normalize to 3NF (SQL)
  • ☐ Define relationships clearly
  • ☐ Add indexes on foreign keys
  • ☐ Use parameterized queries
  • ☐ Implement connection pooling
  • ☐ Plan for scalability
  • ☐ Set up backups
  • ☐ Monitor query performance
  • ☐ Document schema

šŸŽÆ Conclusion

Good database design is foundational to application performance and scalability. Choose the right database type, normalize appropriately, index strategically, and always prioritize security.

šŸ”— Generate Database URLs

Need to create database connection strings? Use our Database URL Generator.

Generate Database URL →