vault backup: 2026-05-21 12:20:54

This commit is contained in:
hhs
2026-05-21 12:20:54 +08:00
parent 2e84683d9c
commit 531d4b7d8c
8 changed files with 867 additions and 89 deletions
@@ -101,6 +101,45 @@ sequenceDiagram
>
> **优先用 `ON DUPLICATE KEY UPDATE`**,除非你确实需要完整的「删+插」语义。
### INSERT ... SELECT
从另一张表或查询结果批量插入数据——这是日常开发中比 `VALUES` 批量插入更高频的用法。
```sql
-- 基础:从查询结果插入
INSERT INTO user_archive (id, username, email, archived_at)
SELECT id, username, email, NOW()
FROM users
WHERE status = 0 AND updated_at < '2025-01-01';
-- 搭配聚合:插入每日统计快照
INSERT INTO daily_stats (stat_date, order_count, total_amount)
SELECT CURDATE(), COUNT(*), SUM(amount)
FROM orders
WHERE DATE(created_at) = CURDATE();
```
> [!QUESTION] INSERT ... SELECT 会锁表吗?
> 这取决于事务隔离级别和索引情况:
> - 在 **RC(Read Committed)** 下:只锁定被插入的目标表,对源表加短暂的共享锁
> - 在 **RR(Repeatable Read)** 下:源表的读取走一致性快照,**不会阻塞源表的写入**
> - 目标表:插入的行会加行锁,大批量插入时注意不要超长事务
> [!CAUTION] INSERT ... SELECT 的两个常见坑
> 1. **SELECT 中不能引用正在被插入的目标表**(MySQL 会报 `Table is specified twice`)。解决办法:嵌套一层子查询
> ```sql
> -- ❌ 报错:直接引用目标表
> INSERT INTO orders_backup SELECT * FROM orders WHERE id IN (SELECT id FROM orders WHERE ...);
> -- ✅ 正确:用子查询隔离
> INSERT INTO orders_backup SELECT * FROM orders WHERE id IN (SELECT t.id FROM (SELECT id FROM orders WHERE ...) t);
> ```
> 2. **大批量插入时分批执行**,避免长事务持有过多锁。可以用 `LIMIT` + 循环分批:
> ```sql
> -- 每次只插入 5000 条,循环直到 affected_rows = 0
> INSERT INTO user_archive
> SELECT * FROM users WHERE status = 0 LIMIT 5000;
> ```
## UPDATE
UPDATE 的核心原则只有一条:**精准定位、最小影响**。先思考「哪些行需要改」,再写 SET 子句。