跪拜 Guibai
← Back to the summary

Deriving a Blog Database from Business Rules, Not Syntax

When learning SQL, the truly difficult part is often not remembering how to write CREATE TABLE, but:

Faced with a real business scenario, which tables should I actually build? What fields go in each table? Why do we need primary keys, indexes, and foreign keys?

This article does not start by memorizing SQL syntax tables. Instead, it assumes we are developing a blog system similar to Juejin, and derives the database structure step by step from the business requirements.


1. Start from the business: What data does a blog system need?

Assume our blog supports these features:

Then the database might contain:

user
avatar
post
user_like_post
user_collect_post
comment
tag
post_tag
file

Don't rush to create all tables at once.

The most important skill in database design is not "writing out all tables in one breath," but:

Breaking down one business requirement at a time.

This article first tackles the three most fundamental ones:

User
Avatar
Article

2. Before designing a table, ask four questions

Whenever you see any business requirement, you can first ask:

1. What does this table store?
2. What does one row of data represent?
3. How do you uniquely find this row?
4. What is its relationship with other tables?

Once these four questions are clear, fields, primary keys, indexes, and foreign keys become much easier to determine.


3. What should the user table store?

The core data of a user system typically includes:

id
username
password

Why not stuff everything like:

avatar
slogan
birthday
address
...

into the user table right from the start?

Because the user table is usually a very high-frequency access table.

For example, login:

SELECT id, username, password
FROM user
WHERE username = ?;

Querying a user:

SELECT *
FROM user
WHERE id = ?;

These operations don't need avatar, bio, or other profile data at all.

Therefore, in some system designs, there is a tendency to:

Keep core identity data in the core user table, and split out extended profile data as needed.

Of course, this doesn't mean "avatar must absolutely be a separate table."

For a small project, you can perfectly well put avatar_url directly in the user table.

The reason for splitting out the avatar here is mainly for learning:


4. Designing the user table

Let's look at a basic version:

CREATE TABLE `user` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `username` VARCHAR(255) NOT NULL,
  `password` VARCHAR(255) NOT NULL,

  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_username` (`username`)
) ENGINE=InnoDB
  DEFAULT CHARSET=utf8mb4
  COLLATE=utf8mb4_unicode_ci;

This table can be understood as:

user

id      username      password
1       zhangsan      ...
2       lisi          ...
3       wangwu        ...

One row of data represents:

A single user.


5. Why do we need an id?

id INT NOT NULL AUTO_INCREMENT

AUTO_INCREMENT means the database can automatically generate:

1
2
3
4
5
...

Then:

PRIMARY KEY (`id`)

means:

id is the unique identifier for this user record.

For example:

id = 12

No matter what the user changes their name to later, user 12 remains user 12.

So in business logic, you typically use:

/user/12

rather than directly using the username as the identity identifier for the whole system.


6. What exactly is a Primary Key?

Primary Key is:

The primary key.

The simplest understanding:

The database uses it to uniquely identify a record.

For example:

id
1
2
3

You cannot have:

1
1

because a primary key cannot be duplicated.

And MySQL will build an index for the primary key, so:

SELECT *
FROM user
WHERE id = 100;

can usually find the target user very quickly.


7. Why does username need UNIQUE?

Usernames typically cannot be duplicated.

For example, if the system already has:

zhangsan

Another person cannot register:

zhangsan

Therefore:

UNIQUE KEY `uk_username` (`username`)

is equivalent to telling the database:

username must be unique.

This way, even if the code layer forgets to check, the database itself will prevent duplicate data.


8. UNIQUE is not just a constraint, it's also an index

This point is very important.

Because during login, you are likely to execute:

SELECT *
FROM user
WHERE username = 'zhangsan';

A unique index exists on username, so the database doesn't need to search through a large number of user records one by one every time.

So UNIQUE serves two roles simultaneously:

Business constraint
Username cannot be duplicated

Query performance
Faster querying by username

9. What exactly is an index?

When first encountering databases, you don't need to dive into the details of B+ Trees immediately.

First, establish the most important intuition:

An index uses extra space to exchange for faster queries.

Suppose the database has millions of users:

SELECT *
FROM user
WHERE username = 'dfp';

If username has no suitable index, the database might need to check a vast number of records.

With an index, the database can locate the target faster.

So when designing indexes, a very important principle is:

Design indexes based on query requirements.

Not:

I feel this field is important, so I'll add an index to it.

Instead, you should think:

By which field does the system frequently query?
How does it frequently sort?
How does it frequently associate?

For example:

GET /user/:id

Frequently finds a user by:

id

The primary key already solves this.

Login frequently finds a user by:

username

The unique index solves this.

This is:

Business queries determine indexes.


10. Why can't passwords be stored in plain text?

Suppose the database directly saves:

username     password

zhangsan     123456
lisi         abc123

Once the database is leaked, all user passwords are directly exposed.

So real systems do not save the user's original password.

They usually save the result processed by a specialized password hashing algorithm, for example using:

bcrypt
Argon2

Such password hashing schemes.

Therefore, the:

password

field in the database is actually more accurately understood as:

password_hash

During login, it's also not:

Taking the original password out of the database to compare.

But rather:

Using the password hashing algorithm to verify if the password entered by the user matches.


11. Where should avatars be stored?

Many beginners, when implementing avatar upload for the first time, think:

Should I just stuff the image directly into the database?

Generally, web projects do not put ordinary image file bodies directly into relational databases.

The more common approach is:

Image file
    ↓
Object storage / Static resource server
    ↓
Get the file address

For example, it might be stored on:

Alibaba Cloud OSS
AWS S3
Tencent Cloud COS
Your own file server

The database mainly saves:

filename
mimetype
size
userId
url / objectKey

and other metadata.


12. Designing the avatar table

For example:

CREATE TABLE `avatar` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `mimetype` VARCHAR(255) NOT NULL,
  `filename` VARCHAR(255) NOT NULL,
  `size` INT NOT NULL,
  `userId` INT NOT NULL,

  PRIMARY KEY (`id`),

  KEY `idx_userId` (`userId`),

  CONSTRAINT `fk_avatar_user`
    FOREIGN KEY (`userId`)
    REFERENCES `user` (`id`)
) ENGINE=InnoDB
  DEFAULT CHARSET=utf8mb4
  COLLATE=utf8mb4_unicode_ci;

Suppose the data is:

id   filename       size     userId
1    abc.webp       10240    7

Translating this into plain language:

abc.webp is the avatar uploaded by user 7.


13. What is userId?

Here appears an extremely important concept in database design:

userId

The avatar table itself doesn't know:

Whom this avatar belongs to.

So it stores a:

userId

pointing to:

user.id

Thus:

user
id = 7

and:

avatar
userId = 7

establish a relationship.


14. What is a Foreign Key?

This segment:

FOREIGN KEY (`userId`)
REFERENCES `user` (`id`)

is the foreign key.

It can be translated as:

avatar.userId references user.id.

For example, if the user table only has:

1
2
3

Then:

avatar.userId = 2

is fine.

But if you insert:

avatar.userId = 999

and the system has no user 999 at all, the database will reject it when foreign key constraints are enabled.

So one of the important roles of a foreign key is:

Ensuring the correctness of data relationships between tables.


15. Why does userId also need an index?

In actual business, you might frequently execute:

SELECT *
FROM avatar
WHERE userId = 7;

That is:

Query the avatar of user 7.

Therefore:

KEY `idx_userId` (`userId`)

helps this query.

Additionally, in MySQL InnoDB, foreign key columns typically also need corresponding indexes to efficiently maintain associations.


16. Next, designing the article table

A blog system certainly needs articles:

post

An article at minimum needs:

id
title
content
userId

Where:

userId

means:

Who wrote this article?


17. The post table

It can be written as:

CREATE TABLE `post` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `title` VARCHAR(255) NOT NULL,
  `content` LONGTEXT,
  `userId` INT NOT NULL,

  PRIMARY KEY (`id`),

  KEY `idx_userId` (`userId`),

  CONSTRAINT `fk_post_user`
    FOREIGN KEY (`userId`)
    REFERENCES `user` (`id`)
) ENGINE=InnoDB
  DEFAULT CHARSET=utf8mb4
  COLLATE=utf8mb4_unicode_ci;

For example:

id    title              userId

101   JavaScript Intro    7
102   MySQL Index Study   7
103   React Hooks         12

This means:

User 7 wrote article 101
User 7 wrote article 102
User 12 wrote article 103

18. Here appears "one-to-many"

Observe:

One user
can write many articles

But:

One article
typically has only one author

So:

user 1 —— N post

This is:

A one-to-many relationship.

The implementation is very simple:

Store the "one" side's id on the "many" side.

That is:

post.userId

pointing to:

user.id

In the future, when you see:

One category has many articles
One department has many employees
One user has many orders

you should think of this design pattern.


19. Primary keys, indexes, and constraints can finally be connected

Now let's re-examine these three tables.

user

id
→ PRIMARY KEY

username
→ UNIQUE

Reason:

id uniquely identifies a user
username cannot be duplicated

avatar

id
→ PRIMARY KEY

userId
→ INDEX
→ FOREIGN KEY

Reason:

id uniquely identifies an avatar record
Frequently query avatar by userId
Avatar must belong to a real user

post

id
→ PRIMARY KEY

userId
→ INDEX
→ FOREIGN KEY

Reason:

id uniquely identifies an article
Frequently query articles of a certain user
Article must be associated with an author

You will find:

SQL syntax does not appear out of thin air.

Every design has a corresponding business reason.


20. What are database constraints really doing?

Constraints can be understood as:

Writing business rules into the database.

For example:

PRIMARY KEY
A record must be uniquely identifiable

UNIQUE
Username cannot be duplicated

NOT NULL
This field must have a value

FOREIGN KEY
Associated data must be valid

A database is not simply "storing data."

A well-designed database should also try its best to prevent incorrect data from entering.


21. What should you really master from this article?

Don't just memorize three CREATE TABLE statements.

What you should really master is this derivation process:

"User" appears
→ user table

A user record must be unique
→ PRIMARY KEY

Username cannot be duplicated
→ UNIQUE

Frequently query by username for login
→ Index

User has an avatar
→ avatar.userId

Avatar must belong to a user
→ FOREIGN KEY

One user can write multiple articles
→ post.userId
→ One-to-many

When you can derive these designs yourself based on the business, SQL has truly started to become accessible.


Summary

The first step in database design is never writing SQL, but understanding the business.

When facing a table, you can repeatedly ask:

What does this table represent?

What does one row of data represent?

What uniquely identifies it?

Which fields cannot be duplicated?

Which fields cannot be empty?

Whom does it belong to?

How does the system frequently query it?

After answering these questions, you can usually gradually derive:

Fields
Primary Key
Unique Constraints
Indexes
Foreign Keys

In the next article, we will continue designing the more interesting parts of the blog system:

Likes
Bookmarks
Comments
Tags
Files

These business requirements will truly introduce very important concepts in database design:

Many-to-many, junction tables, composite primary keys, self-referencing associations, and cascading deletes.