第10章:MySQL 数据库基础
数据库是现代Web应用的核心组件,它负责存储、管理和检索应用程序数据。MySQL作为最受欢迎的开源关系型数据库管理系统,是PHP开发者的首选数据库之一。
学习目标
完成本章学习后,你将能够:
- 理解数据库的基本概念和关系模型
- 掌握MySQL数据库的安装和配置
- 熟练使用SQL语言进行数据操作
- 设计合理的数据库表结构
- 理解数据库索引和性能优化基础
- 掌握PHP与MySQL的连接方法
- 能够创建和管理简单的数据库应用
本章内容概览
数据库基本概念
什么是数据库?
数据库(Database)是按照数据结构来组织、存储和管理数据的仓库。它是一个长期存储在计算机内、有组织的、可共享的大量数据的集合。
关系型数据库的特点
关系型数据库基于关系模型来组织数据,具有以下特点:
- 数据以表格形式存储:数据存储在二维表中
- 表之间的关系:通过键值建立表之间的关联
- ACID特性:保证事务的原子性、一致性、隔离性和持久性
- SQL标准:使用标准化的查询语言
核心概念
-- 数据库:存储数据的容器
CREATE DATABASE myapp;
-- 表:数据的结构化存储
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
email VARCHAR(100) UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 记录:表中的一行数据
INSERT INTO users (username, email) VALUES ('john_doe', 'john@example.com');
-- 字段:表中的一列
SELECT username, email FROM users;
数据库设计原则
1. 实体-关系模型
实体(Entity):现实世界中的对象 属性(Attribute):实体的特征 关系(Relationship):实体之间的联系
2. 范式化
- 第一范式(1NF):字段不可再分
- 第二范式(2NF):满足1NF,非主键字段完全依赖于主键
- 第三范式(3NF):满足2NF,非主键字段不传递依赖于主键
MySQL安装与配置
1. Windows安装
# 下载MySQL安装包
# 访问 https://dev.mysql.com/downloads/mysql/
# 使用MySQL Installer安装
# 配置root密码
# 启动MySQL服务
net start mysql80
2. Linux安装(Ubuntu/Debian)
# 更新包管理器
sudo apt update
# 安装MySQL服务器
sudo apt install mysql-server
# 启动MySQL服务
sudo systemctl start mysql
sudo systemctl enable mysql
# 安全配置
sudo mysql_secure_installation
3. macOS安装
# 使用Homebrew安装
brew install mysql
# 启动MySQL服务
brew services start mysql
# 设置root密码
mysqladmin -u root password 'your_password'
4. 基本配置
# /etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld]
# 设置默认字符集
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
# 设置端口
port = 3306
# 设置最大连接数
max_connections = 100
# 设置查询缓存
query_cache_type = 1
query_cache_size = 16M
# 设置InnoDB缓冲池
innodb_buffer_pool_size = 128M
5. 连接MySQL
# 命令行连接
mysql -u username -p -h hostname database_name
# 连接示例
mysql -u root -p -h localhost mysql
# 查看数据库列表
SHOW DATABASES;
# 退出MySQL
EXIT;
SQL语言基础
SQL(Structured Query Language)是用于管理关系数据库的标准语言。
SQL的分类
- DDL(数据定义语言):CREATE, ALTER, DROP
- DML(数据操作语言):INSERT, UPDATE, DELETE
- DQL(数据查询语言):SELECT
- DCL(数据控制语言):GRANT, REVOKE
- TCL(事务控制语言):COMMIT, ROLLBACK, SAVEPOINT
基本语法规则
-- SQL注释:使用双横线
/*
多行注释
使用斜杠星号
*/
-- 关键字大写,表名和字段名小写(推荐)
SELECT * FROM users WHERE id = 1;
-- 语句以分号结尾
INSERT INTO users (username) VALUES ('john');
-- 字符串使用单引号
SELECT * FROM users WHERE name = 'John Doe';
-- 数值不需要引号
SELECT * FROM users WHERE age > 18;
数据库和表的管理
1. 数据库操作
-- 创建数据库
CREATE DATABASE myapp CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- 查看所有数据库
SHOW DATABASES;
-- 选择数据库
USE myapp;
-- 删除数据库
DROP DATABASE myapp;
-- 修改数据库字符集
ALTER DATABASE myapp CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
2. 表的基本操作
-- 创建表
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL,
password_hash VARCHAR(255) NOT NULL,
age INT,
status ENUM('active', 'inactive', 'suspended') DEFAULT 'inactive',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_username (username),
INDEX idx_email (email)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- 查看表结构
DESCRIBE users;
-- 或者
SHOW COLUMNS FROM users;
-- 查看创建表的SQL语句
SHOW CREATE TABLE users;
-- 修改表结构
-- 添加字段
ALTER TABLE users ADD COLUMN phone VARCHAR(20) AFTER email;
-- 修改字段类型
ALTER TABLE users MODIFY COLUMN phone VARCHAR(25);
-- 修改字段名和类型
ALTER TABLE users CHANGE COLUMN phone mobile_phone VARCHAR(25);
-- 删除字段
ALTER TABLE users DROP COLUMN mobile_phone;
-- 添加索引
ALTER TABLE users ADD INDEX idx_age (age);
-- 删除索引
ALTER TABLE users DROP INDEX idx_age;
-- 重命名表
ALTER TABLE users RENAME TO app_users;
-- 删除表
DROP TABLE users;
-- 清空表数据(保留表结构)
TRUNCATE TABLE users;
3. 数据类型详解
数值类型
-- 整数类型
CREATE TABLE number_types (
tinyint_col TINYINT, -- 1字节,-128到127
smallint_col SMALLINT, -- 2字节,-32768到32767
mediumint_col MEDIUMINT, -- 3字节
int_col INT, -- 4字节,常用
bigint_col BIGINT, -- 8字节
boolean_col BOOLEAN -- TINYINT(1)的别名
);
-- 浮点类型
CREATE TABLE float_types (
float_col FLOAT, -- 单精度浮点数
double_col DOUBLE, -- 双精度浮点数
decimal_col DECIMAL(10,2) -- 定点数,精确到小数点后2位
);
字符串类型
CREATE TABLE string_types (
char_col CHAR(10), -- 固定长度字符串
varchar_col VARCHAR(255), -- 可变长度字符串,常用
text_col TEXT, -- 长文本,最多65535字符
mediumtext_col MEDIUMTEXT, -- 中等文本,最多16777215字符
longtext_col LONGTEXT, -- 长文本,最多4294967295字符
enum_col ENUM('A', 'B', 'C'), -- 枚举类型
set_col SET('A', 'B', 'C') -- 集合类型
);
日期时间类型
CREATE TABLE datetime_types (
date_col DATE, -- 日期 YYYY-MM-DD
time_col TIME, -- 时间 HH:MM:SS
datetime_col DATETIME, -- 日期时间 YYYY-MM-DD HH:MM:SS
timestamp_col TIMESTAMP, -- 时间戳,范围较小
year_col YEAR -- 年份 YYYY
);
-- 插入日期时间数据
INSERT INTO datetime_types VALUES
('2024-01-01', '12:30:45', '2024-01-01 12:30:45', NOW(), 2024);
数据操作语言(DML)
1. 插入数据
-- 单行插入
INSERT INTO users (username, email, password_hash, age, status)
VALUES ('john_doe', 'john@example.com', 'hashed_password', 25, 'active');
-- 多行插入
INSERT INTO users (username, email, age) VALUES
('jane_smith', 'jane@example.com', 23),
('bob_jones', 'bob@example.com', 30),
('alice_brown', 'alice@example.com', 28);
-- 从其他表插入数据
INSERT INTO user_profiles (user_id, bio)
SELECT id, CONCAT('Hello, I am ', username) FROM users WHERE age > 25;
-- 插入时忽略重复
INSERT IGNORE INTO users (username, email) VALUES ('john_doe', 'john2@example.com');
-- 插入时更新重复数据
INSERT INTO users (username, email)
VALUES ('john_doe', 'john3@example.com')
ON DUPLICATE KEY UPDATE email = VALUES(email);
2. 更新数据
-- 更新单个字段
UPDATE users SET status = 'active' WHERE id = 1;
-- 更新多个字段
UPDATE users
SET status = 'active', updated_at = NOW()
WHERE id = 1;
-- 基于条件的更新
UPDATE users SET age = age + 1 WHERE status = 'active';
-- 使用JOIN更新
UPDATE users u
JOIN user_profiles p ON u.id = p.user_id
SET u.status = 'premium'
WHERE p.subscription_level = 'gold';
-- 限制更新的行数
UPDATE users SET status = 'inactive' LIMIT 10;
3. 删除数据
-- 删除特定记录
DELETE FROM users WHERE id = 1;
-- 基于条件的删除
DELETE FROM users WHERE status = 'inactive' AND created_at < '2020-01-01';
-- 删除所有数据(保留表结构)
DELETE FROM users;
-- 或者
TRUNCATE TABLE users;
-- 使用JOIN删除
DELETE users FROM users
JOIN user_profiles ON users.id = user_profiles.user_id
WHERE user_profiles.bio IS NULL;
-- 限制删除的行数
DELETE FROM users WHERE status = 'suspended' LIMIT 5;
数据查询语言(DQL)
1. 基本查询
-- 查询所有字段
SELECT * FROM users;
-- 查询指定字段
SELECT id, username, email FROM users;
-- 使用别名
SELECT
id AS user_id,
username AS name,
email AS email_address
FROM users;
-- 去重查询
SELECT DISTINCT status FROM users;
-- 条件查询
SELECT * FROM users WHERE age > 18;
-- 多条件查询
SELECT * FROM users
WHERE age >= 18 AND status = 'active';
-- 范围查询
SELECT * FROM users WHERE age BETWEEN 20 AND 30;
-- 列表查询
SELECT * FROM users WHERE status IN ('active', 'pending');
-- 模糊查询
SELECT * FROM users WHERE username LIKE 'john%';
SELECT * FROM users WHERE email LIKE '%@gmail.com';
2. 排序和限制
-- 升序排序
SELECT * FROM users ORDER BY age ASC;
-- 降序排序
SELECT * FROM users ORDER BY created_at DESC;
-- 多字段排序
SELECT * FROM users ORDER BY status DESC, age ASC;
-- 限制结果数量
SELECT * FROM users LIMIT 10;
-- 分页查询
SELECT * FROM users LIMIT 10 OFFSET 20;
-- 或者
SELECT * FROM users LIMIT 20, 10;
3. 聚合函数
-- 统计记录数
SELECT COUNT(*) FROM users;
SELECT COUNT(id) FROM users WHERE status = 'active';
-- 计算平均值
SELECT AVG(age) FROM users;
-- 计算总和
SELECT SUM(age) FROM users;
-- 查找最大值和最小值
SELECT MAX(age), MIN(age) FROM users;
-- 分组统计
SELECT status, COUNT(*) as count
FROM users
GROUP BY status;
-- 分组后过滤
SELECT status, COUNT(*) as count
FROM users
GROUP BY status
HAVING COUNT(*) > 5;
4. 连接查询
-- 内连接
SELECT u.username, p.bio
FROM users u
INNER JOIN user_profiles p ON u.id = p.user_id;
-- 左连接
SELECT u.username, p.bio
FROM users u
LEFT JOIN user_profiles p ON u.id = p.user_id;
-- 右连接
SELECT u.username, p.bio
FROM users u
RIGHT JOIN user_profiles p ON u.id = p.user_id;
-- 多表连接
SELECT u.username, p.bio, c.name as city_name
FROM users u
LEFT JOIN user_profiles p ON u.id = p.user_id
LEFT JOIN cities c ON p.city_id = c.id;
5. 子查询
-- WHERE子句中的子查询
SELECT * FROM users
WHERE id IN (SELECT user_id FROM orders WHERE total > 100);
-- FROM子句中的子查询
SELECT u.* FROM (
SELECT * FROM users WHERE age > 18
) AS u WHERE u.status = 'active';
-- EXISTS子查询
SELECT * FROM users u
WHERE EXISTS (
SELECT 1 FROM orders o
WHERE o.user_id = u.id AND o.total > 100
);
数据库设计原则
1. 设计用户管理系统
-- 用户表
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
first_name VARCHAR(50),
last_name VARCHAR(50),
avatar_url VARCHAR(255),
status ENUM('active', 'inactive', 'suspended') DEFAULT 'inactive',
email_verified BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
last_login TIMESTAMP NULL,
INDEX idx_username (username),
INDEX idx_email (email),
INDEX idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- 用户资料表
CREATE TABLE user_profiles (
user_id INT PRIMARY KEY,
bio TEXT,
phone VARCHAR(20),
address TEXT,
city_id INT,
birth_date DATE,
gender ENUM('male', 'female', 'other'),
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (city_id) REFERENCES cities(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- 城市表
CREATE TABLE cities (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
country VARCHAR(100) NOT NULL,
state VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_country (country)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- 用户角色表
CREATE TABLE user_roles (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL UNIQUE,
description TEXT,
permissions JSON,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- 用户角色关联表
CREATE TABLE user_role_assignments (
user_id INT,
role_id INT,
assigned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
assigned_by INT,
PRIMARY KEY (user_id, role_id),
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (role_id) REFERENCES user_roles(id) ON DELETE CASCADE,
FOREIGN KEY (assigned_by) REFERENCES users(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- 插入初始数据
INSERT INTO user_roles (name, description, permissions) VALUES
('admin', '管理员', JSON_OBJECT('users', ['create', 'read', 'update', 'delete'], 'system', ['config', 'logs'])),
('moderator', '版主', JSON_OBJECT('content', ['create', 'read', 'update', 'delete'])),
('user', '普通用户', JSON_OBJECT('profile', ['read', 'update']));
INSERT INTO cities (name, country, state) VALUES
('北京', '中国', '北京市'),
('上海', '中国', '上海市'),
('广州', '中国', '广东省'),
('深圳', '中国', '广东省');
2. 索引优化
-- 创建普通索引
CREATE INDEX idx_username ON users(username);
-- 创建唯一索引
CREATE UNIQUE INDEX idx_email ON users(email);
-- 创建复合索引
CREATE INDEX idx_status_created ON users(status, created_at);
-- 创建全文索引(用于文本搜索)
CREATE FULLTEXT INDEX idx_bio ON user_profiles(bio);
-- 查看表的索引
SHOW INDEX FROM users;
-- 分析查询性能
EXPLAIN SELECT * FROM users WHERE username = 'john_doe';
3. 视图创建
-- 创建用户详细信息视图
CREATE VIEW user_details AS
SELECT
u.id,
u.username,
u.email,
u.first_name,
u.last_name,
u.status,
p.bio,
p.phone,
c.name as city_name,
u.created_at,
u.last_login
FROM users u
LEFT JOIN user_profiles p ON u.id = p.user_id
LEFT JOIN cities c ON p.city_id = c.id;
-- 使用视图
SELECT * FROM user_details WHERE status = 'active';
-- 更新视图(某些情况下)
UPDATE user_details SET bio = 'New bio' WHERE id = 1;
PHP连接MySQL
1. 使用MySQLi扩展
<?php
// 数据库配置
$host = 'localhost';
$username = 'root';
$password = 'your_password';
$database = 'myapp';
// 创建连接
$conn = new mysqli($host, $username, $password, $database);
// 检查连接
if ($conn->connect_error) {
die("连接失败: " . $conn->connect_error);
}
// 设置字符集
$conn->set_charset("utf8mb4");
// 执行查询
$sql = "SELECT id, username, email FROM users WHERE status = 'active'";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
// 输出数据
while($row = $result->fetch_assoc()) {
echo "ID: " . $row["id"]. " - 用户名: " . $row["username"]. " - 邮箱: " . $row["email"]. "<br>";
}
} else {
echo "没有找到记录";
}
// 预处理语句示例
$stmt = $conn->prepare("INSERT INTO users (username, email, password_hash) VALUES (?, ?, ?)");
$stmt->bind_param("sss", $username, $email, $password_hash);
$username = "new_user";
$email = "newuser@example.com";
$password_hash = password_hash("password123", PASSWORD_DEFAULT);
$stmt->execute();
echo "新记录创建成功";
$stmt->close();
$conn->close();
?>
2. 使用PDO扩展(推荐)
<?php
// 数据库配置
$host = 'localhost';
$dbname = 'myapp';
$username = 'root';
$password = 'your_password';
$charset = 'utf8mb4';
// DSN (Data Source Name)
$dsn = "mysql:host=$host;dbname=$dbname;charset=$charset";
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
try {
// 创建PDO实例
$pdo = new PDO($dsn, $username, $password, $options);
// 简单查询
$stmt = $pdo->query("SELECT id, username, email FROM users WHERE status = 'active'");
while ($row = $stmt->fetch()) {
echo "ID: " . $row['id'] . " - 用户名: " . $row['username'] . "<br>";
}
// 预处理语句
$stmt = $pdo->prepare("SELECT * FROM users WHERE username = ? AND status = ?");
$stmt->execute(['john_doe', 'active']);
$user = $stmt->fetch();
// 插入数据
$stmt = $pdo->prepare("INSERT INTO users (username, email, password_hash) VALUES (?, ?, ?)");
$stmt->execute(['new_user', 'newuser@example.com', 'hashed_password']);
// 获取最后插入的ID
$lastId = $pdo->lastInsertId();
echo "新用户ID: " . $lastId;
} catch (PDOException $e) {
die("数据库错误: " . $e->getMessage());
}
?>
3. 数据库连接类
<?php
class Database {
private $pdo;
private $host;
private $dbname;
private $username;
private $password;
private $charset;
public function __construct($host, $dbname, $username, $password, $charset = 'utf8mb4') {
$this->host = $host;
$this->dbname = $dbname;
$this->username = $username;
$this->password = $password;
$this->charset = $charset;
}
public function connect() {
if ($this->pdo === null) {
$dsn = "mysql:host={$this->host};dbname={$this->dbname};charset={$this->charset}";
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_PERSISTENT => true, // 持久连接
];
try {
$this->pdo = new PDO($dsn, $this->username, $this->password, $options);
} catch (PDOException $e) {
throw new Exception("数据库连接失败: " . $e->getMessage());
}
}
return $this->pdo;
}
public function query($sql, $params = []) {
$stmt = $this->connect()->prepare($sql);
$stmt->execute($params);
return $stmt;
}
public function fetch($sql, $params = []) {
return $this->query($sql, $params)->fetch();
}
public function fetchAll($sql, $params = []) {
return $this->query($sql, $params)->fetchAll();
}
public function insert($table, $data) {
$columns = implode(', ', array_keys($data));
$placeholders = implode(', ', array_fill(0, count($data), '?'));
$sql = "INSERT INTO {$table} ({$columns}) VALUES ({$placeholders})";
$this->query($sql, array_values($data));
return $this->pdo->lastInsertId();
}
public function update($table, $data, $where, $whereParams = []) {
$setClauses = [];
$params = [];
foreach ($data as $column => $value) {
$setClauses[] = "{$column} = ?";
$params[] = $value;
}
$sql = "UPDATE {$table} SET " . implode(', ', $setClauses) . " WHERE {$where}";
$params = array_merge($params, $whereParams);
$stmt = $this->query($sql, $params);
return $stmt->rowCount();
}
public function delete($table, $where, $params = []) {
$sql = "DELETE FROM {$table} WHERE {$where}";
$stmt = $this->query($sql, $params);
return $stmt->rowCount();
}
public function beginTransaction() {
return $this->connect()->beginTransaction();
}
public function commit() {
return $this->pdo->commit();
}
public function rollback() {
return $this->pdo->rollBack();
}
}
// 使用示例
try {
$db = new Database('localhost', 'myapp', 'root', 'password');
// 查询用户
$users = $db->fetchAll("SELECT * FROM users WHERE status = ?", ['active']);
// 插入用户
$userId = $db->insert('users', [
'username' => 'test_user',
'email' => 'test@example.com',
'password_hash' => password_hash('password', PASSWORD_DEFAULT)
]);
// 更新用户
$db->update('users',
['status' => 'active'],
'id = ?',
[$userId]
);
// 删除用户
$db->delete('users', 'id = ?', [$userId]);
} catch (Exception $e) {
echo "错误: " . $e->getMessage();
}
?>
实践项目:简单的博客系统
数据库设计
-- 创建博客数据库
CREATE DATABASE blog CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE blog;
-- 用户表
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL UNIQUE,
password_hash VARCHAR(255) NOT NULL,
display_name VARCHAR(100),
avatar_url VARCHAR(255),
bio TEXT,
status ENUM('active', 'inactive') DEFAULT 'active',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- 分类表
CREATE TABLE categories (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
slug VARCHAR(100) NOT NULL UNIQUE,
description TEXT,
parent_id INT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (parent_id) REFERENCES categories(id) ON DELETE SET NULL
);
-- 文章表
CREATE TABLE posts (
id INT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(255) NOT NULL,
slug VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
excerpt TEXT,
author_id INT NOT NULL,
category_id INT,
status ENUM('draft', 'published', 'archived') DEFAULT 'draft',
comment_count INT DEFAULT 0,
view_count INT DEFAULT 0,
featured BOOLEAN DEFAULT FALSE,
published_at TIMESTAMP NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE SET NULL,
INDEX idx_status (status),
INDEX idx_published (published_at),
INDEX idx_author (author_id),
INDEX idx_category (category_id)
);
-- 评论表
CREATE TABLE comments (
id INT PRIMARY KEY AUTO_INCREMENT,
post_id INT NOT NULL,
author_name VARCHAR(100) NOT NULL,
author_email VARCHAR(100) NOT NULL,
author_url VARCHAR(255),
content TEXT NOT NULL,
parent_id INT,
status ENUM('pending', 'approved', 'rejected') DEFAULT 'pending',
ip_address VARCHAR(45),
user_agent TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE,
FOREIGN KEY (parent_id) REFERENCES comments(id) ON DELETE CASCADE
);
-- 插入示例数据
INSERT INTO users (username, email, password_hash, display_name) VALUES
('admin', 'admin@blog.com', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', '管理员'),
('john', 'john@blog.com', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'John Doe');
INSERT INTO categories (name, slug, description) VALUES
('技术', 'tech', '技术相关文章'),
('生活', 'life', '生活随笔'),
('教程', 'tutorial', '教程类文章');
INSERT INTO posts (title, slug, content, author_id, category_id, status, published_at) VALUES
('欢迎来到我的博客', 'welcome-to-my-blog', '这是第一篇文章的内容...', 1, 2, 'published', NOW()),
('PHP基础教程', 'php-basic-tutorial', '今天我们来学习PHP基础...', 1, 3, 'published', NOW());
PHP操作示例
<?php
class Blog {
private $db;
public function __construct($database) {
$this->db = $database;
}
// 获取发布的文章
public function getPublishedPosts($limit = 10, $offset = 0) {
$sql = "SELECT p.*, u.display_name as author_name, c.name as category_name
FROM posts p
LEFT JOIN users u ON p.author_id = u.id
LEFT JOIN categories c ON p.category_id = c.id
WHERE p.status = 'published'
ORDER BY p.published_at DESC
LIMIT ? OFFSET ?";
return $this->db->fetchAll($sql, [$limit, $offset]);
}
// 获取文章详情
public function getPost($slug) {
$sql = "SELECT p.*, u.display_name as author_name, c.name as category_name
FROM posts p
LEFT JOIN users u ON p.author_id = u.id
LEFT JOIN categories c ON p.category_id = c.id
WHERE p.slug = ? AND p.status = 'published'";
$post = $this->db->fetch($sql, [$slug]);
if ($post) {
// 增加浏览次数
$this->db->query("UPDATE posts SET view_count = view_count + 1 WHERE id = ?", [$post['id']]);
}
return $post;
}
// 获取文章评论
public function getPostComments($postId, $approvedOnly = true) {
$sql = "SELECT * FROM comments
WHERE post_id = ?" . ($approvedOnly ? " AND status = 'approved'" : "") . "
ORDER BY created_at ASC";
return $this->db->fetchAll($sql, [$postId]);
}
// 添加评论
public function addComment($postId, $authorName, $authorEmail, $content, $parent_id = null) {
return $this->db->insert('comments', [
'post_id' => $postId,
'author_name' => $authorName,
'author_email' => $authorEmail,
'content' => $content,
'parent_id' => $parent_id,
'ip_address' => $_SERVER['REMOTE_ADDR'] ?? '',
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
'status' => 'pending' // 需要审核
]);
}
// 获取分类列表
public function getCategories() {
return $this->db->fetchAll("SELECT * FROM categories ORDER BY name ASC");
}
// 获取分类下的文章
public function getPostsByCategory($categorySlug, $limit = 10) {
$sql = "SELECT p.*, u.display_name as author_name
FROM posts p
LEFT JOIN users u ON p.author_id = u.id
LEFT JOIN categories c ON p.category_id = c.id
WHERE c.slug = ? AND p.status = 'published'
ORDER BY p.published_at DESC
LIMIT ?";
return $this->db->fetchAll($sql, [$categorySlug, $limit]);
}
// 用户注册
public function registerUser($username, $email, $password, $displayName) {
// 检查用户名和邮箱是否已存在
$existing = $this->db->fetch(
"SELECT id FROM users WHERE username = ? OR email = ?",
[$username, $email]
);
if ($existing) {
throw new Exception("用户名或邮箱已存在");
}
return $this->db->insert('users', [
'username' => $username,
'email' => $email,
'password_hash' => password_hash($password, PASSWORD_DEFAULT),
'display_name' => $displayName
]);
}
// 用户登录验证
public function login($username, $password) {
$user = $this->db->fetch(
"SELECT id, username, email, password_hash, display_name, status
FROM users WHERE username = ? OR email = ? AND status = 'active'",
[$username, $username]
);
if ($user && password_verify($password, $user['password_hash'])) {
return $user;
}
return false;
}
}
// 使用示例
try {
$database = new Database('localhost', 'blog', 'root', 'password');
$blog = new Blog($database);
// 获取最新文章
$posts = $blog->getPublishedPosts(5);
echo "<h2>最新文章</h2>";
foreach ($posts as $post) {
echo "<h3><a href='post.php?slug={$post['slug']}'>" . htmlspecialchars($post['title']) . "</a></h3>";
echo "<p>作者: " . htmlspecialchars($post['author_name']) .
" | 分类: " . htmlspecialchars($post['category_name']) .
" | 浏览: " . $post['view_count'] . "</p>";
echo "<p>" . htmlspecialchars(substr($post['content'], 0, 200)) . "...</p>";
echo "<hr>";
}
} catch (Exception $e) {
echo "错误: " . $e->getMessage();
}
?>
总结
MySQL数据库基础是Web开发的重要技能。通过本章的学习,你应该掌握:
关键知识点:
- 数据库基本概念:理解关系模型、表、记录、字段等概念
- SQL语言:熟练使用DDL、DML、DQL进行数据操作
- 数据库设计:遵循范式化原则,设计合理的表结构
- 索引优化:了解索引的作用和使用场景
- PHP数据库操作:使用MySQLi和PDO扩展连接和操作数据库
最佳实践:
- 使用预处理语句防止SQL注入
- 合理设计索引提高查询性能
- 使用外键约束保证数据完整性
- 定期备份数据库
- 监控和优化慢查询
后续学习方向:
- 深入学习数据库优化和索引调优
- 了解存储过程和触发器
- 学习数据库集群和分库分表
- 掌握数据库备份和恢复
- 学习NoSQL数据库作为补充
通过实践本章的博客系统项目,你可以将理论知识应用到实际开发中,为构建复杂的Web应用奠定坚实的基础。