创建日期:2026-09-08 | 最近更新:2026-09-08 本机 MySQL 8.4.0 实测;下方输出均为真实结果(自动递增 id 延续了此前测试数据,无关紧要)。
MySQL 复习 1:CRUD 与约束
会了增删改查 + 建表约束,你就能在绝大多数后端项目里「干活了」。CRUD = Create / Read / Update / Delete,对应四条 SQL。
1. 四个动词:INSERT / SELECT / UPDATE / DELETE
-- 增:指定列插一行
INSERT INTO customers (name, email, city) VALUES ('李雷', 'lilei@example.com', '深圳');
-- 查:挑列、过滤、排序、限量(用得最多)
SELECT id, name, city FROM customers
WHERE city = '上海'
ORDER BY id DESC
LIMIT 10;
-- 改:务必带 WHERE,否则全表都改!(高危)
UPDATE customers SET city = '广州' WHERE email = 'lilei@example.com';
-- 删:同样务必带 WHERE
DELETE FROM customers WHERE email = 'lilei@example.com';
三条保命习惯:
- UPDATE/DELETE 先写 WHERE 再写别的;不确定就先用同条件 SELECT 看会命中几条;
- 联调/本地常开事务:
BEGIN; …改…;满意再COMMIT,不满意ROLLBACK(篇 4); - 查询尽量只 SELECT 需要的列,别长期
SELECT *(篇 7 讲为什么)。
实测:一条龙的完整输出
INSERT INTO customers(name,email,city) VALUES('李雷','lilei@example.com','深圳');
SELECT id,name,city FROM customers WHERE email='lilei@example.com';
UPDATE customers SET city='广州' WHERE email='lilei@example.com';
SELECT id,city FROM customers WHERE email='lilei@example.com';
DELETE FROM customers WHERE email='lilei@example.com';
SELECT COUNT(*) total FROM customers;
+-----+--------+--------+
| id | name | city |
+-----+--------+--------+
| 514 | 李雷 | 深圳 |
+-----+--------+--------+
+-----+--------+
| id | city |
+-----+--------+
| 514 | 广州 |
+-----+--------+
+-------+
| total |
+-------+
| 300 |
+-------+
关键点:
id是AUTO_INCREMENT自增主键,你 INSERT 不用填,数据库自动给 514;UPDATE 后同一条记录city变了;DELETE 后COUNT(*)回到 300。
2. 主键(PRIMARY KEY):每行的「身份证」
- 建表时指定:
id BIGINT AUTO_INCREMENT PRIMARY KEY; - 作用:唯一标识一行 + 查询/关联用它(底层自动建主键索引,篇 3);
- 好主键的三个标准:不变、唯一、尽可能短(自增整数最省心;UUID 短字符串也可但要考虑性能)。
3. 常用约束一览(建表时给自己「上规矩」)
| 约束 | 作用 | 违反时的结果 |
|---|---|---|
PRIMARY KEY | 唯一 + 非空 + 主索引 | 重复/为空 → 报错 |
UNIQUE | 该列值不许重复(可空,但空只能一个) | 重复 → 报错 1062 |
NOT NULL | 不许为空 | 插入空 → 报错 |
DEFAULT 值 | 不填时用默认值 | — |
CHECK (条件) | 值域检查(8.0 起真生效) | 不满足 → 报错 |
FOREIGN KEY | 引用别表主键,保证关联不出孤儿数据 | 引用不存在 → 报错 1452 |
实测:约束拦住了非法数据
UNIQUE 撞车(想再插一个已存在的 user1@example.com):
ERROR 1062 (23000): Duplicate entry 'user1@example.com' for key 'customers.email'
外键悬空(给不存在的顾客 99999 下单):
ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint
fails (`shop`.`orders`, CONSTRAINT `fk_orders_customer` FOREIGN KEY (`customer_id`)
REFERENCES `customers` (`id`))
这两条报错就是「约束在保护你」:UNIQUE 保证 email 不重复,外键保证订单一定挂在真实存在的顾客上——数据层的脏数据在写入时就被拒了。
编号意义:
1062=唯一冲突、1452=外键失败、1048=NOT NULL、1064=SQL 语法错。看报错先看编号,能少查半天。
4. 约束 vs 应用层校验:都要
- 数据库约束是最后防线(多进程同时写、绕过你代码的脚本都拦得住);
- 应用层(表单/接口校验)负责友好提示;
- 别把业务校验全丢给 DB(报错体验差),也别裸奔只靠应用层(有并发漏洞)。
5. WHERE 的花样(查询基本功)
-- 比较 & 逻辑
WHERE age >= 18 AND city = '上海' OR status = 'vip' -- 注意 AND 优先于 OR,多条件加括号
WHERE id IN (1,2,3) -- 集合内
WHERE email LIKE 'user%' -- 前缀模糊(可走索引)
WHERE email LIKE '%user%' -- 中间模糊(走不了索引,篇 3)
WHERE created_at BETWEEN '2025-01-01' AND '2025-01-31' -- 区间
WHERE city IS NULL -- 判空用 IS NULL,不是 = NULL!
最容易忘:判空是
IS NULL/IS NOT NULL;NULL 参与运算结果是 NULL,所以WHERE x = NULL永远不成立。
6. 建表「规矩清单」(给自己抄)
CREATE TABLE orders (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
customer_id BIGINT NOT NULL,
status VARCHAR(10) NOT NULL DEFAULT 'pending',
amount DECIMAL(10,2) NOT NULL, -- 钱用 DECIMAL,别用 float!
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
KEY idx_customer (customer_id), -- 外键列要有索引(InnoDB 自动给 FK 建)
CONSTRAINT fk_orders_customer FOREIGN KEY (customer_id) REFERENCES customers(id)
) ENGINE=InnoDB;
两个高频踩坑:
- 金额/价格用
DECIMAL(定点),用FLOAT/DOUBLE会有精度误差; - 枚举该用
VARCHAR+ 应用层枚举 还是 MySQLENUM?小范围稳定值可用 ENUM;要扩展用 VARCHAR(改 ENUM 要 DDL 锁表)。
动手
在 shop 里:插入 3 个顾客、把其中 1 个改城市、查上海顾客、删 1 个;再故意插一条重复 email 看 1062;故意插一条不存在 customer_id 的订单看 1452。
自测
- 四条 SQL 动词分别是什么?UPDATE/DELETE 最怕漏什么?
- 1062 / 1452 各代表什么被拦了?
- 判空为什么不能
= NULL? - 为什么金额用 DECIMAL 不用 DOUBLE?
AUTO_INCREMENT要配合什么一起用?
下一篇:查询进阶:JOIN / 聚合 / 子查询。