PostgreSQL导入CSV文件时如何仅写入主键唯一的新增行
PostgreSQL导入CSV自动跳过主键重复行实现方案
前置依赖
首先必须确保存储日报数据的正式表,已经给主键id字段创建了主键约束或唯一约束,这是数据库判断重复的核心依据,没有约束所有去重逻辑都无法生效。
参考建表语句:
CREATE TABLE IF NOT EXISTS daily_report ( id INTEGER PRIMARY KEY, -- 主键字段,类型根据实际业务调整,支持BIGINT、VARCHAR等类型 -- 下方按CSV实际列添加字段即可,例如report_date DATE, trade_amount NUMERIC, remark TEXT col1 VARCHAR, col2 INTEGER, create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP );
通用兼容方案(支持所有PostgreSQL版本)
通过临时表中转导入,再做增量插入,不会残留垃圾数据,逻辑可控:
- 创建和正式表结构完全一致的临时表,临时表仅当前会话可见,会话断开后自动删除:
CREATE TEMP TABLE temp_daily_report (LIKE daily_report INCLUDING ALL);
- 将CSV文件导入临时表,根据自己的文件实际情况调整参数:
COPY temp_daily_report(id, col1, col2) -- 字段顺序必须和CSV内列顺序完全对应 FROM '/your/local/path/daily_report.csv' -- 替换为你的CSV文件绝对路径 WITH ( FORMAT csv, HEADER true, -- CSV第一行是列名表头填true,无表头填false ENCODING 'UTF8' -- 按CSV实际编码调整,Windows导出的文件通常填GBK );
如果是用psql客户端操作本地文件,把COPY换成\copy即可,避免服务端文件权限问题。
- 执行增量插入,仅写入正式表不存在的id对应行:
INSERT INTO daily_report (id, col1, col2) SELECT id, col1, col2 FROM temp_daily_report WHERE NOT EXISTS ( SELECT 1 FROM daily_report WHERE daily_report.id = temp_daily_report.id );
执行完成后提交事务即可,不需要手动清理临时表。
简化写法(PostgreSQL 9.5及以上版本支持)
9.5之后版本原生支持冲突跳过语法,性能更好,写法更简洁,只需要把上述第三步的插入语句替换成下面的内容即可:
INSERT INTO daily_report (id, col1, col2) SELECT id, col1, col2 FROM temp_daily_report ON CONFLICT (id) DO NOTHING;
语句逻辑为:插入时如果碰到id字段冲突(也就是已存在相同id),直接跳过该行不做任何操作,不会报错也不会覆盖原有数据。
特殊情况处理
如果单份CSV文件内部本身就存在重复id的行,可以在插入时先对临时表数据按id去重,避免插入时报错:
INSERT INTO daily_report (id, col1, col2) SELECT DISTINCT ON (id) id, col1, col2 FROM temp_daily_report ON CONFLICT (id) DO NOTHING;
注意:不要直接用COPY命令往正式表导入CSV,一旦碰到重复主键,整个导入操作会直接报错回滚,无法自动跳过重复行。如果用DBeaver、pgAdmin这类可视化工具导入,逻辑完全一致,先把数据导入临时表,再执行增量插入语句即可。
内容的提问来源于stack exchange,提问作者CluelessDawg1337
相关产品推荐
相关产品推荐

