📊 SQL 入门 Day 16:数据插入技巧

📌 今日目标

掌握高级数据插入技巧,告别单条逐行 INSERT,学会高效批量操作与冲突处理。


一、为什么需要「插入技巧」?

场景 1:数据迁移 --- 将旧表数据导入新表,改写表结构或字段名

场景 2:ETL 导入 --- 从 CSV / API 批量写入数万行数据

场景 3:避免重复 --- 插入时检测主键/唯一键冲突,自动更新或忽略

只会 INSERT INTO ... VALUES (...) 的 SQL 是写不坏业务的,但写不出效率。


二、INSERT INTO SELECT:查询结果直接写入

语法

sql 复制代码
INSERT INTO 目标表 (字段1, 字段2, ...)
SELECT 字段1, 字段2, ...
FROM 源表
WHERE 条件;

示例:从 users 表导入 VIP 用户到 vip_users

sql 复制代码
-- 创建 vip_users 表
CREATE TABLE vip_users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50),
    email VARCHAR(100),
    level VARCHAR(20),
    created_at DATETIME
);

-- 将 users 表中积分 > 1000 的用户插入 vip_users
INSERT INTO vip_users (name, email, level, created_at)
SELECT u.name, u.email, '黄金', u.created_at
FROM users u
WHERE u.points > 1000;

执行结果:

java 复制代码
Query OK, 42 rows affected (0.01 sec)
Records: 42  Duplicates: 0  Warnings: 0

注意事项

  • 字段数量和类型必须一一对应
  • 可用表达式、常量、函数,如上面硬编码了 '黄金'
  • 注意字符集一致,避免乱码

三、批量 INSERT:一次写入多行

基础语法

sql 复制代码
INSERT INTO 表 (字段1, 字段2) VALUES
    (值1a, 值2a),
    (值1b, 值2b),
    (值1c, 值2c);

示例:批量插入订单数据

sql 复制代码
INSERT INTO orders (user_id, product, amount, status) VALUES
    (101, '笔记本电脑', 5999.00, '已支付'),
    (102, '机械键盘', 899.00, '待支付'),
    (103, '显示器', 2499.00, '已支付'),
    (104, '鼠标垫', 29.90, '已取消'),
    (105, 'USB Hub', 89.00, '待支付');

性能对比

方式 写入 1000 行 特点
逐条 INSERT ~850ms 每条独立事务,开销大
批量 INSERT(100 条/批) ~45ms 单条 SQL,大幅减少网络与日志 IO
LOAD DATA INFILE ~10ms MySQL 原生高速导入,跳过 SQL 层

经验法则: 单次批量 INSERT 建议 100~500 行,过大可能导致 binlog 膨胀和锁竞争。


四、INSERT IGNORE:跳过重复行

语法

sql 复制代码
INSERT IGNORE INTO 表 (字段列表) VALUES (值列表);

示例

sql 复制代码
CREATE TABLE products (
    sku VARCHAR(20) PRIMARY KEY,
    name VARCHAR(100),
    price DECIMAL(10,2)
);

INSERT IGNORE INTO products (sku, name, price) VALUES
    ('SKU001', '无线鼠标', 129.00),
    ('SKU002', '蓝牙耳机', 399.00),
    ('SKU001', '无线鼠标-副本', 99.00);  -- SKU001 已存在,此行被忽略

执行结果:

java 复制代码
Query OK, 2 rows affected (0.00 sec)
Records: 3  Duplicates: 1  Warnings: 0

第 3 行因主键重复被跳过,其余 2 行插入成功,不报错。


五、ON DUPLICATE KEY UPDATE:冲突时自动更新

语法

sql 复制代码
INSERT INTO 表 (字段列表) VALUES (值列表)
ON DUPLICATE KEY UPDATE
    字段1 = 值1,
    字段2 = 值2;

示例:商品库存 upsert

sql 复制代码
CREATE TABLE inventory (
    sku VARCHAR(20) PRIMARY KEY,
    stock INT NOT NULL,
    updated_at DATETIME
);

-- 插入或更新库存
INSERT INTO inventory (sku, stock, updated_at) VALUES ('SKU001', 50, NOW())
ON DUPLICATE KEY UPDATE
    stock = stock + VALUES(stock),    -- VALUES() 引用本次插入的值
    updated_at = VALUES(updated_at);

多次执行后:

sql 复制代码
-- 第 1 次执行 → 插入 (SKU001, 50)
-- 第 2 次执行 → 更新 stock = 50 + 50 = 100
-- 第 3 次执行 → 更新 stock = 100 + 50 = 150

VALUES(字段) 是 MySQL 语法,引用本次 INSERT 部分的对应值。MySQL 8.0 起推荐使用别名方式:

sql 复制代码
INSERT INTO inventory (sku, stock, updated_at) VALUES ('SKU001', 50, NOW()) AS new
ON DUPLICATE KEY UPDATE
    stock = inventory.stock + new.stock,
    updated_at = new.updated_at;

实战:用户积分流水 upsert

sql 复制代码
INSERT INTO user_points (user_id, total_points, last_updated) VALUES (1001, 200, NOW())
ON DUPLICATE KEY UPDATE
    total_points = total_points + VALUES(total_points),
    last_updated = VALUES(last_updated);

六、REPLACE INTO:删除再插入

语法

sql 复制代码
REPLACE INTO 表 (字段列表) VALUES (值列表);

示例

sql 复制代码
REPLACE INTO products (sku, name, price) VALUES
    ('SKU001', '无线鼠标-升级版', 159.00);

⚠️ 与 ON DUPLICATE KEY UPDATE 的区别

行为 REPLACE ON DUPLICATE KEY UPDATE
冲突时 先 DELETE 再 INSERT 直接 UPDATE
自增 ID 会变化(删除重建) 保持不变
触发器 DELETE + INSERT 各触发一次 仅 UPDATE 触发器
性能 较差(删除+插入两倍开销) 较好(原地更新)

结论: 绝大多数场景优先用 ON DUPLICATE KEY UPDATE,除非你确实想重置整行数据。


七、各数据库冲突处理语法对照

数据库 语法
MySQL ON DUPLICATE KEY UPDATE
PostgreSQL ON CONFLICT (字段) DO UPDATE SET ...
SQLite ON CONFLICT(字段) DO UPDATE SET ...
SQL Server MERGE ... WHEN MATCHED THEN UPDATE

八、综合实战:每日销售汇总

sql 复制代码
-- 销售明细表
CREATE TABLE daily_sales_summary (
    sale_date DATE NOT NULL,
    product_id INT NOT NULL,
    total_qty INT DEFAULT 0,
    total_amount DECIMAL(12,2) DEFAULT 0,
    PRIMARY KEY (sale_date, product_id)
);

-- 从订单明细汇总插入,冲突则累加
INSERT INTO daily_sales_summary (sale_date, product_id, total_qty, total_amount)
SELECT
    DATE(order_time),
    product_id,
    SUM(quantity),
    SUM(quantity * price)
FROM order_items
WHERE order_time >= '2026-07-24' AND order_time < '2026-07-25'
GROUP BY DATE(order_time), product_id
ON DUPLICATE KEY UPDATE
    total_qty = daily_sales_summary.total_qty + VALUES(total_qty),
    total_amount = daily_sales_summary.total_amount + VALUES(total_amount);

九、避坑指南 🚧

陷阱 说明
批量插入过大 单条 SQL 超过 max_allowed_packet(默认 64MB)会报错
忽略 timezone VALUES(NOW()) 在冲突更新时用的是第二次插入的时间,而非当前时间
主键自增跳跃 使用 ON DUPLICATE KEY UPDATE 冲突时自增计数器仍会 +1,造成 ID 空洞
REPLACE 副作用 会删除索引外的其他行(外键约束下可能失败)

📝 今日总结

  • INSERT INTO ... SELECT --- 数据迁移/复制的利器
  • 批量 INSERT --- 大幅提升写入性能
  • INSERT IGNORE --- 跳过重复,静默容错
  • ON DUPLICATE KEY UPDATE --- "有则更新,无则插入"的 Upsert 模式
  • REPLACE --- 慎用,有副作用

思考题: 假设你有一个日志表,每秒写入 1000 条。你会怎么设计插入方案来保证性能?提示:想想批量写入 + 表分区。

相关推荐
小码哥_常4 小时前
一套自己部署的 IM,客户端是同一份 Flutter
前端·后端
Decay__4 小时前
05_MySQL 常用 SQL 大全 —— 诊断、管理、优化辅助 SQL 与三大系统库速查
运维·数据库·sql·mysql·架构
学长毕设7 小时前
基于SpringBoot的实验室资产管理系统
java·spring boot·后端
努力的小雨9 小时前
给 AI 眼镜开发应用有多难?我从零做了一个陪练,还上架了
后端
Decay__10 小时前
04_MySQL DBA 日常运维手册 —— 会话管理、锁与阻塞、事务监控、慢查询与日志巡检
运维·数据库·sql·mysql·架构
excel10 小时前
双 Token 与前端使用 Crypto 公私钥鉴权换 Token,哪种方案更好?
前端·后端
AI入职记11 小时前
AI 代码审查召回率实测:20 个骗过单元测试的变异体,抓到 13 个
后端
浮链序11 小时前
别让 AI 客服把用户当猴耍:百来行 Python 把「转人工」做成一条高可用链路
后端
属于自己的天空11 小时前
Claude Code 插件完全指南:官方市场怎么用,怎么自己写(v2.1.269+)
后端
Tokenfree11 小时前
给大模型调用加一个熔断器:60 行代码,三个阈值
后端