📊 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 条。你会怎么设计插入方案来保证性能?提示:想想批量写入 + 表分区。

相关推荐
山峰哥2 分钟前
数据库工程与查询优化案例深度复盘‌
数据库·sql·oracle·编辑器·深度优先·宽度优先
jay神15 分钟前
【计算机毕业设计】基于Spring Boot的宠物领养管理系统
java·spring boot·后端·vue·毕业设计·宠物
2601_9620566027 分钟前
可造成敏感信息泄露!Spring Boot之Actuator信息泄露漏洞三种利用方式总结
java·spring boot·后端
码视野35 分钟前
基于 Spring Boot + Vue3 的【城市道路地下空洞塌陷微动传感监测与路面脱空沉降预警中台】设计与实现(含PRD/三端高保真源码/大屏)
vue.js·spring boot·后端
2601_962181963 小时前
Spring boot从0到1 - day01
java·spring boot·后端
码视野3 小时前
基于 Spring Boot + Vue3 的【智慧公厕微负压排风除臭与客流人感空间占用导引中台】设计与实现(含PRD/三端高保真源码/大屏)
前端·vue.js·人工智能·spring boot·后端
茶栀(*´I`*)3 小时前
数据库零基础入门指南:从基本概念到 SQL 实战
数据库·sql
ERD Online3 小时前
Cursor 连上 MCP:读一张 ER 图,提交一版建议
数据库·后端·开源·cursor·mcp
思考着亮3 小时前
4.依赖注入_Depends用法_四大应用场景_Yield生成器原理.
后端
思考着亮4 小时前
3.全局中间件_洋葱模型执行机制_跨域CORS配置
后端