MySQL 简介

MySQL 是最流行的开源关系型数据库。数据以"表"的形式组织,表由行(记录)和列(字段)构成,通过 SQL 语言进行增删改查。

SQL 基础

-- 建库建表
CREATE DATABASE shop CHARACTER SET utf8mb4;

USE shop;

CREATE TABLE users (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(50) NOT NULL,
  age INT DEFAULT 0,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- 插入
INSERT INTO users (name, age) VALUES ('小明', 25);

-- 查询
SELECT * FROM users WHERE age > 18 ORDER BY id DESC LIMIT 10;

-- 更新 / 删除
UPDATE users SET age = 26 WHERE name = '小明';
DELETE FROM users WHERE id = 1;
✅ 建议
生产环境避免 SELECT *,只取需要的字段;WHERE 条件要能用上索引。

多表查询

-- 内连接:两表都匹配的记录
SELECT u.name, o.total
FROM users u
JOIN orders o ON u.id = o.user_id;

-- 左连接:左表全部 + 右表匹配
SELECT u.name, COUNT(o.id) AS order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
GROUP BY u.id
HAVING order_count > 0;

常用聚合函数:COUNT、SUM、AVG、MAX、MIN,配合 GROUP BY 分组统计。

数据库基本概念

数据库是结构化存储数据的软件。MySQL 是最流行的开源关系型数据库,几乎每个 Web 项目都在用。

关系型数据库的层次结构:

层级说明类比
数据库(Database)一个项目一套库仓库
表(Table)同类型数据的集合货架
行(Row)一条记录一件货物
列(Column)一个字段货物属性

表与表之间通过主键(PK)和外键(FK)建立关联,例如"用户表"与"订单表"通过 user_id 关联。

💡 提示
MySQL 的 SQL 关键字不区分大小写,但习惯上关键字大写、表名小写,便于阅读。

连接与库操作

连接 MySQL(命令行方式):

# 连接本机
mysql -u root -p

# 连接远程(宝塔面板等)
mysql -h 59.110.240.202 -P 3306 -u site -p

库操作

-- 查看所有库
SHOW DATABASES;

-- 创建库(utf8mb4 支持中文与 emoji)
CREATE DATABASE mydb DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci;

-- 切换/使用库
USE mydb;

-- 查看库下所有表
SHOW TABLES;

-- 删除库(危险!)
DROP DATABASE mydb;
⚠️ 注意
生产环境字符集务必用 utf8mb4,否则插入 emoji 或生僻字会报错。

创建表与约束

CREATE TABLE users (
    id         INT AUTO_INCREMENT PRIMARY KEY,  -- 主键自增
    name       VARCHAR(50)  NOT NULL,           -- 必填
    email      VARCHAR(120) UNIQUE,             -- 唯一约束
    age        TINYINT      DEFAULT 0,          -- 默认值
    status     TINYINT      DEFAULT 1,
    created_at DATETIME     DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_name (name)                       -- 普通索引
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

常用数据类型速查:

类型用途
INT / BIGINT整数
VARCHAR(n)变长字符串
TEXT长文本(文章正文)
DECIMAL(10,2)金额等精确小数
DATETIME日期时间
BOOLEAN布尔(实际为 TINYINT)
✅ 建议
主键统一用 id INT AUTO_INCREMENT;金额永远不要用 FLOAT,精度会丢失。

插入数据 INSERT

-- 单条插入
INSERT INTO users (name, email, age)
VALUES ('小明', 'xm@example.com', 25);

-- 批量插入(效率高)
INSERT INTO users (name, email) VALUES
    ('小红', 'xh@example.com'),
    ('小刚', 'xg@example.com');

-- 插入并获取自增 ID
INSERT INTO users (name) VALUES ('小丽');
SELECT LAST_INSERT_ID();
✅ 建议
批量插入用一条 INSERT 带多组 VALUES,比循环逐条插入快数十倍。

查询 SELECT 基础

-- 查询指定列
SELECT name, email FROM users;

-- 查询所有列(慎用,生产不推荐)
SELECT * FROM users;

-- 条件过滤
SELECT * FROM users
WHERE age >= 18 AND status = 1;

-- 去重
SELECT DISTINCT city FROM users;

-- 起别名
SELECT name AS 姓名, age AS 年龄 FROM users;

-- 限制条数
SELECT * FROM users ORDER BY id DESC LIMIT 10;

-- 模糊查询(% 任意字符)
SELECT * FROM users WHERE name LIKE '小%';

-- 区间查询
SELECT * FROM users WHERE age BETWEEN 18 AND 30;

-- IN 列表
SELECT * FROM users WHERE city IN ('北京', '上海');
⚠️ 注意
线上环境避免 SELECT *,显式列出需要的列,减少传输与索引负担。

更新与删除

-- 更新(务必带 WHERE!)
UPDATE users SET age = 26 WHERE id = 1;

-- 多字段更新
UPDATE users
SET status = 0, updated_at = NOW()
WHERE status = 1 AND age > 60;

-- 删除(务必带 WHERE!)
DELETE FROM users WHERE id = 10;

-- 清空表(保留结构,重置自增)
TRUNCATE TABLE users;
⚠️ 危险操作预警
UPDATE / DELETE 不带 WHERE 会作用于全表!养成习惯:先 SELECT 确认影响行数,再执行 UPDATE/DELETE。

执行前确认影响范围:

-- 先查
SELECT COUNT(*) FROM users WHERE age > 60;
-- 再删
DELETE FROM users WHERE age > 60;

排序与聚合

-- 排序
SELECT * FROM orders
ORDER BY created_at DESC;      -- 倒序(最新在前)

-- 多条件排序
SELECT * FROM products
ORDER BY category_id ASC, price DESC;

-- 聚合函数:COUNT / SUM / AVG / MAX / MIN
SELECT
    COUNT(*)          AS 订单数,
    SUM(amount)       AS 总金额,
    AVG(amount)       AS 平均金额,
    MAX(amount)       AS 最大单,
    MIN(amount)       AS 最小单
FROM orders;
函数作用
COUNT(*)行数
SUM(col)求和
AVG(col)平均值
MAX / MIN最大 / 最小

分组 GROUP BY

分组统计是最常用的分析手段:GROUP BY 按某列分组,配合聚合函数统计每组:

-- 按城市统计用户数
SELECT city, COUNT(*) AS cnt
FROM users
GROUP BY city
ORDER BY cnt DESC;

-- 按分类统计销售额,只保留总额 > 1000 的分组
SELECT category_id, SUM(price * qty) AS total
FROM order_items
GROUP BY category_id
HAVING total > 1000;

-- WHERE 分组前过滤,HAVING 分组后过滤
SELECT status, COUNT(*)
FROM orders
WHERE created_at >= '2026-01-01'   -- 先过滤
GROUP BY status
HAVING COUNT(*) > 5;               -- 再分组过滤
⚠️ 注意
WHERE 在分组前过滤行,HAVING 在分组后过滤组,两者不能互换。

索引原理

索引好比书的目录:没有索引,MySQL 要全表扫描(逐行找);有索引,直接定位到目标行,速度提升几个数量级。

MySQL 默认使用 B+ 树索引,底层结构不用深究,记住它的特性:

  • 等值查询、范围查询、排序都能加速
  • 索引列越多,写入越慢(每次插入都要维护索引)
  • 索引不是越多越好
💡 提示
用 EXPLAIN 看执行计划:EXPLAIN SELECT * FROM users WHERE email='x@y.com'; 观察 type 列:ALL 是全表扫描(危险),ref/range/const 说明用到了索引。

创建与管理索引

-- 建表时创建(前面已见)
-- 单独创建
CREATE INDEX idx_email ON users(email);
CREATE UNIQUE INDEX uk_phone ON users(phone);   -- 唯一索引

-- 联合索引(最左前缀原则)
CREATE INDEX idx_city_age ON users(city, age);

-- 查看表的索引
SHOW INDEX FROM users;

-- 删除索引
DROP INDEX idx_email ON users;

最左前缀原则:联合索引 (city, age) 可以命中 WHERE city=... 或 WHERE city=... AND age=...,但单独 WHERE age=... 无法使用该索引。

✅ 建议
高频查询的 WHERE 列、JOIN 的关联列、ORDER BY 的列,是建立索引的首选目标。

慢查询优化套路

一条 SQL 变慢,按以下顺序排查:

问题对策
全表扫描给 WHERE / JOIN 列加索引
SELECT * 取多余列只查需要的列
LIKE '%xx%' 前置通配无法走索引,改用其他方案
函数包裹索引列WHERE DATE(created_at)='2026-01-01' 会失效,改用范围: WHERE created_at >= '2026-01-01' AND created_at < '2026-01-02'
大表分页过深用 WHERE id > 10000 LIMIT 20 替代 OFFSET
-- 深分页优化:游标式分页(快得多)
-- ❌ SELECT * FROM posts LIMIT 100000, 20;
-- ✅
SELECT * FROM posts
WHERE id > (SELECT id FROM posts ORDER BY id LIMIT 1 OFFSET 99999)
ORDER BY id LIMIT 20;
⚠️ 注意
优化前先用 EXPLAIN 看执行计划,不要凭感觉加索引;数据量小时先关注查询逻辑是否正确。
第 1 / 14 节