数据插入 #
一、INSERT基础 #
1.1 基本语法 #
sql
-- 基本插入语法
INSERT INTO table_name (column1, column2, ...)
VALUES (value1, value2, ...);
-- 示例
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(100) NOT NULL,
email VARCHAR(100),
age INT,
created_at TIMESTAMP DEFAULT NOW()
);
-- 插入单行数据
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 (gen_random_uuid(), 'Bob', 'bob@example.com', 30, NOW());
-- 省略列名,按顺序插入所有列
INSERT INTO users
VALUES (gen_random_uuid(), '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 UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(100),
email VARCHAR(100),
age INT,
backup_time TIMESTAMP DEFAULT NOW()
);
-- 从源表复制数据
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 UUID PRIMARY KEY,
user_id BIGINT,
amount DECIMAL(10, 2),
status VARCHAR(20),
created_at TIMESTAMP,
archived_at TIMESTAMP DEFAULT NOW()
);
-- 归档旧数据
INSERT INTO orders_archive (id, user_id, amount, status, created_at)
SELECT id, user_id, amount, status, created_at
FROM orders
WHERE created_at < NOW() - INTERVAL '1 year';
-- 删除已归档数据
DELETE FROM orders
WHERE created_at < NOW() - INTERVAL '1 year';
3.3 数据转换插入 #
sql
-- 创建汇总表
CREATE TABLE user_stats (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
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 ON CONFLICT #
4.1 存在则更新 #
sql
-- 创建表
CREATE TABLE user_scores (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id BIGINT UNIQUE,
score INT DEFAULT 0,
updated_at TIMESTAMP DEFAULT NOW()
);
-- 插入或更新 (UPSERT)
INSERT INTO user_scores (user_id, score)
VALUES (1, 100)
ON CONFLICT (user_id)
DO UPDATE SET
score = user_scores.score + EXCLUDED.score,
updated_at = NOW();
-- 第一次执行: 插入新记录
-- 第二次执行: 更新已有记录
SELECT * FROM user_scores;
4.2 存在则忽略 #
sql
-- 创建有唯一约束的表
CREATE TABLE emails (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
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 key value violates unique constraint
-- 使用 ON CONFLICT DO NOTHING 忽略
INSERT INTO emails (email, user_id)
VALUES ('test@example.com', 2)
ON CONFLICT DO NOTHING;
-- 不报错,也不插入
-- 查看结果
SELECT * FROM emails;
4.3 批量UPSERT #
sql
-- 批量插入或更新
INSERT INTO user_scores (user_id, score) VALUES
(1, 100),
(2, 200),
(3, 300)
ON CONFLICT (user_id)
DO UPDATE SET
score = EXCLUDED.score,
updated_at = NOW();
-- 使用 EXCLUDED 引用新值
INSERT INTO user_scores (user_id, score) VALUES
(1, 50),
(2, 60)
ON CONFLICT (user_id)
DO UPDATE SET
score = user_scores.score + EXCLUDED.score;
4.4 实际应用场景 #
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 CONFLICT (page_id)
DO UPDATE SET
view_count = page_views.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 CONFLICT (product_id)
DO UPDATE SET
quantity = inventory.quantity + EXCLUDED.quantity;
五、UPSERT语句 #
5.1 UPSERT语法 #
sql
-- UPSERT 是 INSERT ON CONFLICT 的简写
-- 存在则更新,不存在则插入
-- 使用 UPSERT
UPSERT INTO user_scores (user_id, score)
VALUES (1, 500);
-- 等价于
INSERT INTO user_scores (user_id, score)
VALUES (1, 500)
ON CONFLICT (user_id)
DO UPDATE SET score = EXCLUDED.score;
-- 批量 UPSERT
UPSERT INTO user_scores (user_id, score) VALUES
(1, 100),
(2, 200),
(3, 300);
5.2 UPSERT vs INSERT ON CONFLICT #
text
UPSERT vs INSERT ON CONFLICT
┌─────────────────────────────────────────────────────────────┐
│ │
│ UPSERT: │
│ ├── 语法简洁 │
│ ├── 总是更新所有列 │
│ └── 适合简单场景 │
│ │
│ INSERT ON CONFLICT: │
│ ├── 语法灵活 │
│ ├── 可以指定更新哪些列 │
│ ├── 可以使用 DO NOTHING │
│ └── 适合复杂场景 │
│ │
│ 示例对比: │
│ │
│ -- UPSERT: 更新所有列 │
│ UPSERT INTO users (id, name, email) │
│ VALUES (1, 'Alice', 'new@example.com'); │
│ │
│ -- INSERT ON CONFLICT: 只更新 email │
│ INSERT INTO users (id, name, email) │
│ VALUES (1, 'Alice', 'new@example.com') │
│ ON CONFLICT (id) DO UPDATE SET │
│ email = EXCLUDED.email; │
│ │
└─────────────────────────────────────────────────────────────┘
六、导入数据 #
6.1 IMPORT INTO #
sql
-- 从CSV文件导入
IMPORT INTO users (name, email, age)
CSV DATA (
'nodelocal://1/users.csv'
);
-- 带选项导入
IMPORT INTO users (name, email, age)
CSV DATA (
'nodelocal://1/users.csv'
)
WITH
skip = '1', -- 跳过第一行
delimiter = ',', -- 分隔符
quote = '"', -- 引号
nullif = ''; -- 空值处理
-- 从多个文件导入
IMPORT INTO users (name, email, age)
CSV DATA (
'nodelocal://1/users1.csv',
'nodelocal://1/users2.csv'
);
6.2 使用COPY #
sql
-- 从客户端复制数据
COPY users (name, email, age) FROM STDIN WITH CSV HEADER;
Alice,alice@example.com,25
Bob,bob@example.com,30
\.
-- 导出数据
COPY users TO STDOUT WITH CSV HEADER;
七、性能优化 #
7.1 插入性能建议 #
text
插入性能优化建议
┌─────────────────────────────────────────────────────────────┐
│ │
│ 1. 使用批量插入 │
│ ├── 单条语句插入多行 │
│ └── 推荐每批 100-1000 行 │
│ │
│ 2. 减少索引数量 │
│ ├── 插入时维护索引有开销 │
│ └── 大量导入前可先删除索引 │
│ │
│ 3. 使用事务 │
│ ├── 多条插入放在一个事务中 │
│ └── 减少事务提交次数 │
│ │
│ 4. 使用 IMPORT │
│ ├── 比 INSERT 更快 │
│ └── 适合大数据量导入 │
│ │
│ 5. 避免热点 │
│ ├── 使用 UUID 代替自增ID │
│ └── 分散写入压力 │
│ │
└─────────────────────────────────────────────────────────────┘
7.2 事务批量插入 #
sql
-- 使用事务批量插入
BEGIN;
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;
八、常见问题 #
8.1 主键冲突 #
sql
-- 问题: 插入重复主键
INSERT INTO users (id, name) VALUES ('uuid-1', 'Duplicate');
-- Error: duplicate key value violates unique constraint
-- 解决方案1: 使用 ON CONFLICT DO NOTHING
INSERT INTO users (id, name) VALUES ('uuid-1', 'Duplicate')
ON CONFLICT (id) DO NOTHING;
-- 解决方案2: 使用 ON CONFLICT DO UPDATE
INSERT INTO users (id, name) VALUES ('uuid-1', 'Duplicate')
ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name;
-- 解决方案3: 使用 UPSERT
UPSERT INTO users (id, name) VALUES ('uuid-1', 'Duplicate');
8.2 字段类型不匹配 #
sql
-- 问题: 类型不匹配
INSERT INTO users (name, age) VALUES ('Test', 'not_a_number');
-- Error: invalid input syntax for type int
-- 解决方案: 确保数据类型正确
INSERT INTO users (name, age) VALUES ('Test', 25);
-- 或使用类型转换
INSERT INTO users (name, age) VALUES ('Test', CAST('25' AS INT));
8.3 字段长度超限 #
sql
-- 问题: 字段值超长
INSERT INTO users (name) VALUES ('This is a very long name that exceeds the column limit...');
-- Error: value too long for type character varying(100)
-- 解决方案: 调整字段长度或截断数据
ALTER TABLE users ALTER COLUMN name TYPE VARCHAR(200);
-- 或使用函数截断
INSERT INTO users (name) VALUES (SUBSTRING('very long name...', 1, 100));
九、总结 #
INSERT 语句要点:
| 类型 | 说明 |
|---|---|
| 基本插入 | 单行、多行、指定列 |
| 批量插入 | 多行 VALUES,性能优化 |
| INSERT SELECT | 从其他表复制数据 |
| ON CONFLICT | 存在则更新或忽略 |
| UPSERT | 简化的插入或更新 |
| IMPORT | 文件导入 |
下一步,让我们学习数据更新操作!
最后更新:2026-03-27