PostgreSQL 数据插入 #
INSERT 概述 #
INSERT 语句用于向表中插入新数据。
text
┌─────────────────────────────────────────────────────────────┐
│ INSERT 语句结构 │
├─────────────────────────────────────────────────────────────┤
│ │
│ INSERT INTO table_name (column1, column2, ...) │
│ VALUES (value1, value2, ...) │
│ [ RETURNING * | column1, column2, ... ]; │
│ │
│ 组成部分: │
│ ├── INSERT INTO table_name 指定目标表 │
│ ├── (columns) 指定列(可选) │
│ ├── VALUES (values) 指定值 │
│ └── RETURNING 返回插入的数据(可选) │
│ │
└─────────────────────────────────────────────────────────────┘
基本插入 #
插入单行数据 #
sql
-- 创建示例表
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE,
age INTEGER,
status VARCHAR(20) DEFAULT 'active',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 插入完整行(指定所有列)
INSERT INTO users (id, name, email, age, status, created_at)
VALUES (DEFAULT, 'Alice', 'alice@example.com', 25, 'active', DEFAULT);
-- 插入部分列(未指定的列使用默认值或 NULL)
INSERT INTO users (name, email, age)
VALUES ('Bob', 'bob@example.com', 30);
-- 只插入必填列
INSERT INTO users (name)
VALUES ('Charlie');
-- 使用 DEFAULT 关键字
INSERT INTO users (name, email, status)
VALUES ('David', 'david@example.com', DEFAULT);
省略列名 #
sql
-- 省略列名时,必须按表定义顺序提供所有列的值
INSERT INTO users
VALUES (DEFAULT, 'Eve', 'eve@example.com', 28, 'active', DEFAULT);
-- 不推荐:列顺序变化会导致错误
批量插入 #
多行 VALUES #
sql
-- 一次插入多行
INSERT INTO users (name, email, age) VALUES
('Frank', 'frank@example.com', 35),
('Grace', 'grace@example.com', 28),
('Henry', 'henry@example.com', 42);
-- 批量插入性能更好
-- 比多次单行插入快得多
从查询插入 #
sql
-- 创建备份表
CREATE TABLE users_backup (
id INTEGER,
name VARCHAR(100),
email VARCHAR(100),
backup_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 从查询结果插入
INSERT INTO users_backup (id, name, email)
SELECT id, name, email FROM users WHERE status = 'active';
-- 从另一个表复制数据
CREATE TABLE users_archive (LIKE users);
INSERT INTO users_archive
SELECT * FROM users WHERE created_at < '2025-01-01';
RETURNING 子句 #
PostgreSQL 特有的 RETURNING 子句可以返回插入的数据:
sql
-- 返回所有列
INSERT INTO users (name, email, age)
VALUES ('Ivan', 'ivan@example.com', 33)
RETURNING *;
-- 输出:
-- id | name | email | age | status | created_at
-- ----+-------+--------------------+-----+--------+----------------------------
-- 8 | Ivan | ivan@example.com | 33 | active | 2026-03-29 10:00:00.000000
-- 返回特定列
INSERT INTO users (name, email)
VALUES ('Julia', 'julia@example.com')
RETURNING id, name, created_at;
-- 返回生成的 ID
INSERT INTO users (name, email)
VALUES ('Kevin', 'kevin@example.com')
RETURNING id;
-- 在应用中使用
-- INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id;
插入冲突处理(UPSERT) #
ON CONFLICT 语法 #
sql
INSERT INTO table_name (column1, column2, ...)
VALUES (value1, value2, ...)
ON CONFLICT [conflict_target] conflict_action;
-- conflict_target:
-- (column_name) 指定冲突列
-- ON CONSTRAINT constraint_name 指定约束名
-- WHERE predicate 条件
-- conflict_action:
-- DO NOTHING 忽略冲突
-- DO UPDATE SET ... 更新数据
DO NOTHING #
sql
-- 如果 email 冲突,忽略插入
INSERT INTO users (name, email, age)
VALUES ('Alice', 'alice@example.com', 26)
ON CONFLICT (email) DO NOTHING;
-- 使用约束名
INSERT INTO users (name, email)
VALUES ('Alice', 'alice@example.com')
ON CONFLICT ON CONSTRAINT users_email_key DO NOTHING;
DO UPDATE(UPSERT) #
sql
-- 如果 email 冲突,更新数据
INSERT INTO users (name, email, age)
VALUES ('Alice', 'alice@example.com', 26)
ON CONFLICT (email)
DO UPDATE SET
name = EXCLUDED.name,
age = EXCLUDED.age,
status = 'updated';
-- EXCLUDED 表包含尝试插入的数据
-- 使用条件更新
INSERT INTO users (name, email, age)
VALUES ('Alice', 'alice@example.com', 26)
ON CONFLICT (email)
DO UPDATE SET
age = EXCLUDED.age
WHERE users.age < EXCLUDED.age; -- 只在新年龄更大时更新
-- 返回结果
INSERT INTO users (name, email, age)
VALUES ('Alice', 'alice@example.com', 26)
ON CONFLICT (email)
DO UPDATE SET age = EXCLUDED.age
RETURNING id, name, (xmax = 0) AS inserted;
-- inserted = true 表示新插入
-- inserted = false 表示更新
COPY 命令 #
从文件导入 #
sql
-- 创建表
CREATE TABLE products (
id INTEGER,
name VARCHAR(100),
price DECIMAL(10, 2),
category VARCHAR(50)
);
-- 从 CSV 文件导入
COPY products FROM '/path/to/products.csv'
DELIMITER ','
CSV HEADER;
-- 从文本文件导入
COPY products FROM '/path/to/products.txt'
DELIMITER E'\t';
-- 指定列
COPY products (name, price, category)
FROM '/path/to/products.csv'
DELIMITER ',' CSV HEADER;
-- 指定编码
COPY products FROM '/path/to/products.csv'
DELIMITER ','
CSV HEADER
ENCODING 'UTF8';
导出数据 #
sql
-- 导出为 CSV
COPY users TO '/path/to/users.csv'
DELIMITER ','
CSV HEADER;
-- 导出查询结果
COPY (
SELECT id, name, email
FROM users
WHERE status = 'active'
) TO '/path/to/active_users.csv'
DELIMITER ','
CSV HEADER;
-- 导出为文本
COPY users TO '/path/to/users.txt'
DELIMITER E'\t';
-- 导出二进制格式
COPY users TO '/path/to/users.bin' WITH BINARY;
COPY 参数说明 #
text
┌─────────────────┬─────────────────────────────────────────────┐
│ 参数 │ 说明 │
├─────────────────┼─────────────────────────────────────────────┤
│ DELIMITER │ 分隔符,默认制表符 │
│ CSV │ CSV 格式 │
│ HEADER │ 第一行是列名 │
│ QUOTE │ 引号字符,默认 " │
│ ESCAPE │ 转义字符,默认 " │
│ NULL │ NULL 值表示,默认 \N │
│ ENCODING │ 文件编码 │
│ BINARY │ 二进制格式 │
└─────────────────┴─────────────────────────────────────────────┘
客户端 COPY #
sql
-- \copy 是 psql 客户端命令,不需要超级用户权限
-- 从文件导入
\copy products FROM '/path/to/products.csv' CSV HEADER
-- 导出到文件
\copy users TO '/path/to/users.csv' CSV HEADER
-- 导出查询结果
\copy (SELECT * FROM users WHERE status = 'active') TO '/path/to/active_users.csv' CSV HEADER
插入默认值 #
sql
-- 使用 DEFAULT VALUES 插入所有默认值
CREATE TABLE logs (
id SERIAL PRIMARY KEY,
message TEXT DEFAULT 'No message',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
level VARCHAR(10) DEFAULT 'INFO'
);
INSERT INTO logs DEFAULT VALUES;
-- 混合使用
INSERT INTO logs (message) VALUES ('Error occurred');
插入特殊值 #
sql
-- 插入 NULL
INSERT INTO users (name, email, age)
VALUES ('Test', NULL, NULL);
-- 插入表达式结果
INSERT INTO users (name, email, created_at)
VALUES (
'Generated',
'gen_' || md5(random()::TEXT) || '@example.com',
CURRENT_TIMESTAMP + INTERVAL '1 day'
);
-- 插入数组
CREATE TABLE projects (
id SERIAL PRIMARY KEY,
name VARCHAR(100),
tags VARCHAR(50)[]
);
INSERT INTO projects (name, tags) VALUES
('Project A', ARRAY['web', 'frontend']),
('Project B', '{"backend", "api"}');
-- 插入 JSON
CREATE TABLE settings (
id SERIAL PRIMARY KEY,
config JSONB
);
INSERT INTO settings (config) VALUES
('{"theme": "dark", "language": "zh-CN"}'),
('{"theme": "light", "language": "en-US"}');
性能优化 #
批量插入优化 #
sql
-- 不推荐:循环单行插入
-- INSERT INTO users (name) VALUES ('User1');
-- INSERT INTO users (name) VALUES ('User2');
-- INSERT INTO users (name) VALUES ('User3');
-- 推荐:批量插入
INSERT INTO users (name) VALUES
('User1'),
('User2'),
('User3');
-- 大批量数据使用 COPY
COPY users (name, email) FROM '/path/to/large_data.csv' CSV;
禁用索引和约束 #
sql
-- 大批量插入前禁用索引
DROP INDEX idx_users_email;
-- 插入数据
COPY users FROM '/path/to/users.csv' CSV;
-- 重建索引
CREATE INDEX idx_users_email ON users(email);
-- 或使用事务
BEGIN;
ALTER TABLE users DROP CONSTRAINT users_email_key;
COPY users FROM '/path/to/users.csv' CSV;
ALTER TABLE users ADD CONSTRAINT users_email_key UNIQUE (email);
COMMIT;
使用 UNLOGGED 表 #
sql
-- 创建 UNLOGGED 表(不写 WAL 日志,更快但崩溃会丢失数据)
CREATE UNLOGGED TABLE temp_data (
id SERIAL PRIMARY KEY,
data TEXT
);
-- 批量插入
INSERT INTO temp_data (data)
SELECT 'data_' || generate_series(1, 100000);
-- 处理完后转为普通表
ALTER TABLE temp_data SET LOGGED;
常见错误处理 #
违反约束 #
sql
-- 主键冲突
-- ERROR: duplicate key value violates unique constraint "users_pkey"
INSERT INTO users (id, name) VALUES (1, 'Duplicate');
-- 解决:使用 ON CONFLICT 或跳过重复值
-- 外键约束失败
-- ERROR: insert or update on table "orders" violates foreign key constraint
INSERT INTO orders (user_id, amount) VALUES (999, 100);
-- 解决:确保外键值存在
-- 非空约束
-- ERROR: null value in column "name" violates not-null constraint
INSERT INTO users (name, email) VALUES (NULL, 'test@example.com');
-- 解决:提供值或修改约束
类型转换错误 #
sql
-- 类型不匹配
-- ERROR: invalid input syntax for type integer
INSERT INTO users (name, age) VALUES ('Test', 'not_a_number');
-- 解决:确保类型正确或显式转换
INSERT INTO users (name, age) VALUES ('Test', '25'::INTEGER);
学习路径 #
text
基础阶段
├── 数据库操作
├── 表操作
├── 数据插入(本文)
└── 数据更新
下一步 #
掌握了数据插入后,接下来学习 数据更新,了解如何修改已有数据!
最后更新:2026-03-29