使用序列列
您计划为 4000 万行添加一个不必要的巨大索引。而且您甚至不确定它是否唯一。我强烈建议不要采取这种行动。相反,添加一个 serial
列,并完成它:
ALTER TABLE tbl ADD COLUMN tbl_id serial PRIMARY KEY;
这就是你需要做的全部,其余部分将自动完成。手册中或这些密切相关的答案中有更多信息:
PostgreSQL主键自动递增在C++中崩溃
自动递增SQL函数
添加一个serial
列是一次性操作,但代价高昂。整个表必须被重写,操作期间会阻塞更新。最好在非高峰期无并发负载时完成。我在这里引用手册:
添加具有非空默认值的列或更改现有列的类型将要求重新编写整个表和索引。[...] 对于大型表,表格和/或索引重建可能需要大量时间;并且暂时需要多达两倍的磁盘空间。
由于这实际上重新编写了整个表,因此您可以创建一个带有序列pk列的新表,从旧表中插入所有行,让序列使用其序列中的默认值填充,删除旧表并重命名新表。更多相关答案:
在PostgreSQL 9.2中更新数据库行而不锁定表
添加新列而不锁定表?
确保所有INSERT语句都有目标列表,然后额外的列就不会使它们混淆:
INSERT INTO tbl (col1, col2, ...) VALUES ...
注意:
INSERT INTO tbl VALUES ...
serial
是使用一个 integer
列(4个字节)实现的。
主键约束是通过在相关列上实施唯一索引和 NOT NULL
约束来实现的。
索引的内容存储方式很像表格。需要另外的物理存储空间。有关物理存储的更多信息,请参阅此相关答案:
在 PostgreSQL 中计算和保存空间
您的索引将包含 2 个时间戳(2 x 8 字节)以及一个包括路径在内的较长的文件名(大约 50 字节?)。这将使索引增加约 2.5 GB(40M x 60..某些字节),并使所有操作变慢。
处理重复项
如何处理“导入重复项”取决于您导入数据的方式以及如何定义“重复项”。
如果我们讨论的是COPY
语句,则一种方法是使用临时暂存表,并在INSERT
命令中使用简单的SELECT DISTINCT
或DISTINCT ON
合并重复项:
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) 如果所有列中存在重复项,则根本不能添加主键。
2) 我不会接触一个完全过时、效率低下、不再受支持并可能存在许多未修补安全漏洞的PostgreSQL数据库 8.1版本。官方Postgres版本网站
@David已经提供了SQL语句。
3和4) 重复键违规。 PostgreSQL抛出错误也意味着整个事务会回滚。 在perl脚本中捕获它无法使其余事务继续进行。 您必须创建一个具有plpgsql的服务器端脚本,例如可以在其中捕获异常。