将多列主键添加到包含4000万条记录的表中

时间:2013-06-13 13:48:02

标签: postgresql primary-key

我正在努力维护一个在不同网络之间存储数据传输信息的数据库。本质上,每个数据传输都会被记录,并且在每个月末我运行一个perl脚本,将日志文件加载到数据库中的表中。我没有设计perl脚本或数据库架构。这是在我开始这个项目之前完成的。

我使用this链接来检索表的主键(usage_detail是表的名称),它什么都没给我。由于表中有如此多的记录,因此不容易跟踪重复记录。我们遇到了很多重复加载的问题(因为错误脚本为每次传输执行日志记录,但这是另一个主题的日志)并且最终必须删除最新加载并在修复日志文件后重新加载所有新加载。你可能已经猜到这是愚蠢而乏味的。

要解决这个问题,我想在表格中添加一个主键。由于多种原因,我们不希望为主键添加整个新列。看完这些字段后,我发现了一个多列主键。基本上它包括:传输开始时间戳,传输结束时间戳,传输的文件名(也包括整个路径)。似乎不太可能存在两个具有相同字段的记录。

以下是我的问题: 1)如果我在表中添加了这个主键,那么表中可能已存在的任何重复项会发生什么?

2)我如何将这个主键实际添加到表中(我们使用的是PostgreSQL 8.1.22)。

3)添加主键后,假设在加载脚本运行时它尝试加载副本。 PostgreSQL会抛出什么样的错误?我能在脚本中捕获它吗?

4)我知道你没有太多关于加载脚本的信息,但是根据我提供的信息,您预见到脚本中可能需要更改的内容吗?

非常感谢任何帮助。 感谢。

2 个答案:

答案 0 :(得分:2)

  1. 您将无法使用重复项添加它。您需要先删除重复项。
  2. ALTER TABLE foo ADD CONSTRAINT foo_pkey PRIMARY KEY(fld1,fld2);
  3. PostgreSQL会给你一个SQL状态代码的错误:23505。我 我对Perl一无所知,但我认为你可以陷阱 此。
  4. 再一次,对perl一无所知,但我认为你可以 抓住错误,然后由你来决定该怎么办 它。
  5. 注意:您使用的是不受支持的PostgreSQL版本(您可能应该升级)。我甚至无法在SqlFiddle上测试它。因此,所有答案都是基于PosgreSQL 9.1

    给出的

答案 1 :(得分:2)

使用串行列

你的计划是为4000万(!)行添加一个不必要的巨大索引。你甚至不确定它会是独一无二的。我强烈反对这种行动方案。添加serial列,然后完成它:

ALTER TABLE tbl ADD COLUMN tbl_id serial PRIMARY KEY;

这就是你需要做的一切。其余的事情自动发生。更多内容在手册或这些密切相关的答案中:
PostgreSQL primary key auto increment crashes in C++
Auto increment SQL function

添加serial列是一次性操作,但价格昂贵。必须重写整个表,在操作期间阻止更新。最好在非工作时间没有并发负载。我引用the manual here

  

添加具有非null默认值的列或更改其类型   现有列将需要整个表和索引   重写。 [...]表和/或索引重建可能会很重要   大桌子的时间量;并暂时要求尽可能多   作为磁盘空间的两倍。

由于这有效地重写了整个表,你不妨创建一个带有串行pk列的新表,插入旧表中的所有行,让序列填充其序列中的默认值,删除旧表并重命名新。更多这些密切相关的答案:
Updating database rows without locking the table in PostgreSQL 9.2
Add new column without table lock?

确保所有INSERT语句都有目标列表,然后其他列不能混淆它们:

INSERT INTO tbl (col1, col2, ...) VALUES ...

INSERT INTO tbl VALUES ...

使用serial列(4个字节)实现integer 使用唯一索引和所涉及列的NOT NULL约束实现主键约束 索引的内容与表格非常相似。需要额外的物理存储空间。更多关于物理存储的相关答案:
Calculating and saving space in PostgreSQL

您的索引将包含2个时间戳(2 x 8字节)加上冗长的文件名,包括。路径(~50个字节?)这将使索引大约2.5 GB(40M x 60 ..某些字节)并且所有操作都更慢。

处理重复

如何处理“导入重复项”取决于您如何导入数据以及如何精确定义“重复”。

如果我们讨论COPY语句,一种方法是使用临时登台表并使用SELECT DISTINCT命令中的简单DISTINCT ONINSERT折叠重复项:

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 。它毫无希望地古老,过时和低效,不再支持,并且可能有许多不固定的安全漏洞。 Official Postgres versioning site.
 @David已经提供了SQL语句。

3& 4)重复密钥违规。抛出错误的PostgreSQL也意味着整个事务被回滚。在perl脚本中捕获它不能使事务的其余部分通过。例如,您必须使用plpgsql创建服务器端脚本,您可以在其中捕获异常。