数据插入 #

一、INSERT基础 #

1.1 基本语法 #

sql
-- 基本插入语法
INSERT INTO table_name (column1, column2, ...)
VALUES (value1, value2, ...);

-- 示例
CREATE TABLE users (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100) NOT NULL,
    email VARCHAR(100),
    age INT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 插入单行数据
INSERT INTO users (name, email, age)
VALUES ('Alice', 'alice@example.com', 25);

-- 查询结果
SELECT * FROM users;

1.2 插入完整行 #

sql
-- 插入所有列
INSERT INTO users (id, name, email, age, created_at)
VALUES (NULL, 'Bob', 'bob@example.com', 30, NOW());

-- 省略列名,按顺序插入所有列
INSERT INTO users
VALUES (NULL, 'Carol', 'carol@example.com', 28, NOW());

-- 使用 DEFAULT 关键字
INSERT INTO users (name, email, age, created_at)
VALUES ('David', 'david@example.com', 35, DEFAULT);

1.3 指定列插入 #

sql
-- 只插入部分列,其他列使用默认值
INSERT INTO users (name, email)
VALUES ('Eve', 'eve@example.com');

-- age 和 created_at 使用默认值
SELECT * FROM users WHERE name = 'Eve';

二、批量插入 #

2.1 多行插入 #

sql
-- 单条语句插入多行
INSERT INTO users (name, email, age) VALUES
    ('Frank', 'frank@example.com', 32),
    ('Grace', 'grace@example.com', 27),
    ('Henry', 'henry@example.com', 29),
    ('Ivy', 'ivy@example.com', 24);

-- 查询结果
SELECT * FROM users;

2.2 批量插入优化 #

text
批量插入性能优化
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│   单条插入 vs 批量插入:                                     │
│                                                             │
│   单条插入 (多条语句):                                      │
│   INSERT INTO users VALUES (...);  -- 语句1                │
│   INSERT INTO users VALUES (...);  -- 语句2                │
│   INSERT INTO users VALUES (...);  -- 语句3                │
│                                                             │
│   问题:                                                     │
│   - 每条语句都有网络开销                                    │
│   - 每条语句都有事务开销                                    │
│   - 性能较差                                                │
│                                                             │
│   ─────────────────────────────────────────────────────     │
│                                                             │
│   批量插入 (单条语句):                                      │
│   INSERT INTO users VALUES (...), (...), (...);            │
│                                                             │
│   优势:                                                     │
│   - 单次网络传输                                           │
│   - 单次事务提交                                           │
│   - 性能提升 10-100x                                       │
│                                                             │
└─────────────────────────────────────────────────────────────┘

2.3 最佳批量大小 #

sql
-- 推荐批量大小: 100-1000 行每批次
-- 过大可能导致内存问题或超时

-- 示例: 分批插入大量数据
-- 批次1
INSERT INTO users (name, email, age) VALUES
    ('User001', 'user001@example.com', 20),
    ('User002', 'user002@example.com', 21),
    -- ... 更多行
    ('User500', 'user500@example.com', 25);

-- 批次2
INSERT INTO users (name, email, age) VALUES
    ('User501', 'user501@example.com', 26),
    ('User502', 'user502@example.com', 27),
    -- ... 更多行
    ('User1000', 'user1000@example.com', 30);

三、INSERT SELECT #

3.1 从其他表复制数据 #

sql
-- 创建目标表
CREATE TABLE users_backup (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100),
    email VARCHAR(100),
    age INT,
    backup_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 从源表复制数据
INSERT INTO users_backup (name, email, age)
SELECT name, email, age FROM users;

-- 带条件复制
INSERT INTO users_backup (name, email, age)
SELECT name, email, age 
FROM users 
WHERE age > 25;

3.2 表间数据迁移 #

sql
-- 创建归档表
CREATE TABLE orders_archive (
    id BIGINT PRIMARY KEY,
    user_id BIGINT,
    amount DECIMAL(10, 2),
    status VARCHAR(20),
    created_at TIMESTAMP,
    archived_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 归档旧数据
INSERT INTO orders_archive (id, user_id, amount, status, created_at)
SELECT id, user_id, amount, status, created_at
FROM orders
WHERE created_at < DATE_SUB(NOW(), INTERVAL 1 YEAR);

-- 删除已归档数据
DELETE FROM orders 
WHERE created_at < DATE_SUB(NOW(), INTERVAL 1 YEAR);

3.3 数据转换插入 #

sql
-- 创建汇总表
CREATE TABLE user_stats (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id BIGINT,
    total_orders INT,
    total_amount DECIMAL(20, 2),
    avg_amount DECIMAL(10, 2),
    stat_date DATE
);

-- 聚合数据插入
INSERT INTO user_stats (user_id, total_orders, total_amount, avg_amount, stat_date)
SELECT 
    user_id,
    COUNT(*) AS total_orders,
    SUM(amount) AS total_amount,
    AVG(amount) AS avg_amount,
    CURRENT_DATE AS stat_date
FROM orders
WHERE status = 'completed'
GROUP BY user_id;

四、INSERT IGNORE #

4.1 忽略重复数据 #

sql
-- 创建有唯一约束的表
CREATE TABLE emails (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    email VARCHAR(100) UNIQUE,
    user_id BIGINT
);

-- 插入数据
INSERT INTO emails (email, user_id) VALUES ('test@example.com', 1);

-- 普通插入会报错
INSERT INTO emails (email, user_id) VALUES ('test@example.com', 2);
-- Error: Duplicate entry 'test@example.com' for key 'email'

-- 使用 INSERT IGNORE 忽略错误
INSERT IGNORE INTO emails (email, user_id) 
VALUES ('test@example.com', 2);
-- Query OK, 0 rows affected (不会报错,但也不插入)

-- 查看结果
SELECT * FROM emails;

4.2 批量忽略重复 #

sql
-- 批量插入时忽略重复
INSERT IGNORE INTO emails (email, user_id) VALUES
    ('new@example.com', 3),
    ('test@example.com', 4),  -- 重复,被忽略
    ('another@example.com', 5);

-- 查看结果
SELECT * FROM emails;

五、INSERT ON DUPLICATE KEY UPDATE #

5.1 存在则更新 #

sql
-- 创建表
CREATE TABLE user_scores (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id BIGINT UNIQUE,
    score INT DEFAULT 0,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

-- 插入或更新
INSERT INTO user_scores (user_id, score) 
VALUES (1, 100)
ON DUPLICATE KEY UPDATE 
    score = score + 100,
    updated_at = NOW();

-- 第一次执行: 插入新记录
-- 第二次执行: 更新已有记录

SELECT * FROM user_scores;

5.2 使用VALUES函数 #

sql
-- VALUES() 获取要插入的值
INSERT INTO user_scores (user_id, score) 
VALUES (1, 50)
ON DUPLICATE KEY UPDATE 
    score = VALUES(score),  -- 使用新值覆盖
    updated_at = NOW();

-- 批量插入或更新
INSERT INTO user_scores (user_id, score) VALUES
    (1, 100),
    (2, 200),
    (3, 300)
ON DUPLICATE KEY UPDATE 
    score = VALUES(score),
    updated_at = NOW();

5.3 实际应用场景 #

sql
-- 计数器场景
CREATE TABLE page_views (
    page_id BIGINT PRIMARY KEY,
    view_count BIGINT DEFAULT 0,
    last_view TIMESTAMP
);

-- 每次访问增加计数
INSERT INTO page_views (page_id, view_count, last_view)
VALUES (1, 1, NOW())
ON DUPLICATE KEY UPDATE 
    view_count = view_count + 1,
    last_view = NOW();

-- 库存更新场景
CREATE TABLE inventory (
    product_id BIGINT PRIMARY KEY,
    quantity INT DEFAULT 0
);

-- 入库
INSERT INTO inventory (product_id, quantity)
VALUES (1, 100)
ON DUPLICATE KEY UPDATE 
    quantity = quantity + VALUES(quantity);

六、REPLACE语句 #

6.1 REPLACE语法 #

sql
-- REPLACE: 存在则删除后插入,不存在则直接插入
REPLACE INTO user_scores (user_id, score)
VALUES (1, 500);

-- 注意: REPLACE 会删除旧行再插入新行
-- 自增ID会变化,触发器也会触发

-- 查看结果
SELECT * FROM user_scores;

6.2 REPLACE vs INSERT ON DUPLICATE KEY UPDATE #

text
REPLACE vs INSERT ON DUPLICATE KEY UPDATE
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│   REPLACE:                                                  │
│   ├── 存在重复键时: 删除旧行 + 插入新行                    │
│   ├── 自增ID会变化                                         │
│   ├── 触发 DELETE + INSERT 触发器                          │
│   └── 可能影响关联数据                                     │
│                                                             │
│   INSERT ON DUPLICATE KEY UPDATE:                           │
│   ├── 存在重复键时: 更新旧行                               │
│   ├── 自增ID不变                                           │
│   ├── 触发 UPDATE 触发器                                   │
│   └── 不影响关联数据                                       │
│                                                             │
│   推荐使用: INSERT ON DUPLICATE KEY UPDATE                 │
│                                                             │
└─────────────────────────────────────────────────────────────┘

七、LOAD DATA导入 #

7.1 基本语法 #

sql
-- 从文件导入数据
LOAD DATA LOCAL INFILE '/path/to/users.csv'
INTO TABLE users
FIELDS TERMINATED BY ',' 
ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 LINES
(name, email, age);

-- CSV 文件示例:
-- name,email,age
-- Alice,alice@example.com,25
-- Bob,bob@example.com,30
-- Carol,carol@example.com,28

7.2 导入选项 #

sql
-- 完整导入示例
LOAD DATA LOCAL INFILE '/path/to/data.csv'
INTO TABLE users
FIELDS 
    TERMINATED BY ','      -- 字段分隔符
    ENCLOSED BY '"'        -- 字段引号
    ESCAPED BY '\\'        -- 转义字符
LINES 
    TERMINATED BY '\n'     -- 行分隔符
    STARTING BY ''         -- 行起始符
IGNORE 1 LINES             -- 忽略首行(标题)
(name, email, age)         -- 列映射
SET created_at = NOW();    -- 设置额外列

7.3 使用TiDB Lightning #

bash
# 对于大数据量导入,推荐使用 TiDB Lightning
# 配置文件 lightning.toml

[lightning]
level = "info"

[tikv-importer]
backend = "local"  # 或 "tidb"

[mydumper]
data-source-dir = "/path/to/csv/files"

[checkpoint]
enable = true

# 执行导入
tidb-lightning -config lightning.toml

八、性能优化 #

8.1 插入性能建议 #

text
插入性能优化建议
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│   1. 使用批量插入                                           │
│   ├── 单条语句插入多行                                     │
│   └── 推荐每批 100-1000 行                                 │
│                                                             │
│   2. 减少索引数量                                           │
│   ├── 插入时维护索引有开销                                 │
│   └── 大量导入前可先删除索引                               │
│                                                             │
│   3. 使用事务                                               │
│   ├── 多条插入放在一个事务中                               │
│   └── 减少事务提交次数                                     │
│                                                             │
│   4. 关闭约束检查                                           │
│   ├── 大量导入时可临时关闭                                 │
│   └── 导入完成后恢复                                       │
│                                                             │
│   5. 使用 LOAD DATA                                         │
│   ├── 比 INSERT 更快                                       │
│   └── 适合大数据量导入                                     │
│                                                             │
│   6. 使用 TiDB Lightning                                    │
│   ├── 最快的导入方式                                       │
│   └── 适合 TB 级数据                                       │
│                                                             │
└─────────────────────────────────────────────────────────────┘

8.2 事务批量插入 #

sql
-- 使用事务批量插入
START TRANSACTION;

INSERT INTO users (name, email, age) VALUES
    ('User001', 'user001@example.com', 20),
    ('User002', 'user002@example.com', 21),
    -- ... 更多行
    ('User500', 'user500@example.com', 25);

INSERT INTO users (name, email, age) VALUES
    ('User501', 'user501@example.com', 26),
    ('User502', 'user502@example.com', 27),
    -- ... 更多行
    ('User1000', 'user1000@example.com', 30);

COMMIT;

九、常见问题 #

9.1 主键冲突 #

sql
-- 问题: 插入重复主键
INSERT INTO users (id, name) VALUES (1, 'Duplicate');
-- Error: Duplicate entry '1' for key 'PRIMARY'

-- 解决方案1: 使用 INSERT IGNORE
INSERT IGNORE INTO users (id, name) VALUES (1, 'Duplicate');

-- 解决方案2: 使用 ON DUPLICATE KEY UPDATE
INSERT INTO users (id, name) VALUES (1, 'Duplicate')
ON DUPLICATE KEY UPDATE name = VALUES(name);

-- 解决方案3: 使用 REPLACE
REPLACE INTO users (id, name) VALUES (1, 'Duplicate');

9.2 字段类型不匹配 #

sql
-- 问题: 类型不匹配
INSERT INTO users (name, age) VALUES ('Test', 'not_a_number');
-- Error: Incorrect integer value

-- 解决方案: 确保数据类型正确
INSERT INTO users (name, age) VALUES ('Test', 25);

-- 或使用类型转换
INSERT INTO users (name, age) VALUES ('Test', CAST('25' AS SIGNED));

9.3 字段长度超限 #

sql
-- 问题: 字段值超长
INSERT INTO users (name) VALUES ('This is a very long name that exceeds the column limit...');
-- Error: Data too long for column 'name'

-- 解决方案: 调整字段长度或截断数据
ALTER TABLE users MODIFY name VARCHAR(200);

-- 或使用函数截断
INSERT INTO users (name) VALUES (LEFT('very long name...', 100));

十、总结 #

INSERT 语句要点:

类型 说明
基本插入 单行、多行、指定列
批量插入 多行 VALUES,性能优化
INSERT SELECT 从其他表复制数据
INSERT IGNORE 忽略重复错误
ON DUPLICATE KEY UPDATE 存在则更新
REPLACE 删除后插入
LOAD DATA 文件导入

下一步,让我们学习数据更新操作!

最后更新:2026-03-27