使用串行列
您的计划是为 4000 万(!)行添加一个不必要的巨大索引。你甚至不确定它是否是独一无二的。我强烈反对这种做法。改为添加一serial
列并完成它:
ALTER TABLE tbl ADD COLUMN tbl_id serial PRIMARY KEY;
这就是你需要做的。其余的会自动发生。更多手册或这些密切相关的答案:
PostgreSQL primary key auto increment crashs in C++
Auto increment SQL function
添加serial
列是一次性操作,但成本很高。必须重写整个表,在操作期间阻止更新。最好在非工作时间没有并发负载的情况下完成。我在这里引用手册:
添加具有非空默认值的列或更改现有列的类型将需要重写整个表和索引。[...] 表和/或索引重建可能需要大量时间来处理大型表;并且将暂时需要两倍的磁盘空间。
由于这有效地重写了整个表,您不妨创建一个具有串行 pk 列的新表,插入旧表中的所有行,让串行填充其序列中的默认值,删除旧表并重命名新表。这些密切相关的答案中的更多内容:
更新数据库行而不锁定 PostgreSQL 9.2 中的表
添加没有表锁定的新列?
确保您的所有 INSERT 语句都有一个目标列表,然后附加列不会混淆它们:
INSERT INTO tbl (col1, col2, ...) VALUES ...
不是:
插入 tbl 值...
Aserial
用一integer
列(4 个字节)实现。
主键约束通过唯一索引和NOT NULL
对相关列的约束来实现。
索引的内容存储起来很像表。单独需要额外的物理存储。有关此相关答案中物理存储的更多信息:
在 PostgreSQL 中计算和节省空间
您的索引将包括 2 个时间戳(2 x 8 字节)加上一个冗长的文件名,包括。路径(~ 50 字节?)这将使索引大约 2.5 GB 更大(40M x 60 .. 一些字节)并且所有操作都变慢。
处理重复项
如何处理“导入重复”取决于您如何导入数据以及如何准确定义“重复”。
如果我们在谈论COPY
语句,一种方法是使用临时临时表并使用简单的SELECT DISTINCT
或DISTINCT ON
在INSERT
命令中折叠重复项:
CREATE TEMP TABLE tbl_tmp AS
SELECT * FROM tbl LIMIT 0; -- copy structure without data and constraints
COPY tbl_tmp FROM '/path/to/file.csv';
INSERT INTO tbl (col1, col2, col3)
SELECT DISTINCT ON (col1, col2)
col1, col2, col3 FROM tbl_tmp;
或者,也禁止与已经存在的行重复:
INSERT INTO tbl (col1, col2, col3)
SELECT i.*
FROM (
SELECT DISTINCT ON (col1, col2)
col1, col2, col3
FROM tbl_tmp
) i
LEFT JOIN tbl t USING (col1, col2)
WHERE t.col1 IS NULL;
温度。表在会话结束时自动删除。
但正确的解决方法是首先处理产生重复的错误的根源。
原始问题
1) 如果所有列都有一个重复项,则根本无法添加 pk。
2) 我只会用一根 5 英尺长的杆子碰 PostgreSQL 数据库8.1 版。它非常古老、过时且效率低下,不再受支持,并且可能有许多未修复的安全漏洞。官方 Postgres 版本控制站点。
@David已经提供了 SQL 语句。
3 & 4) 重复密钥违规。PostgreSQL 抛出错误也意味着整个事务被回滚。在 perl 脚本中捕获它不能使其余的事务通过。例如,您必须使用 plpgsql 创建一个服务器端脚本,您可以在其中捕获异常。