vault backup: 2026-05-21 12:20:54
This commit is contained in:
@@ -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 子句。
|
||||
|
||||
Reference in New Issue
Block a user