3

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

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

为了解决这个问题,我想在表中添加一个主键。由于几个原因,我们不想为主键添加一个全新的列。查看字段后,我找到了一个多列主键。基本上它包括:传输开始时间戳、传输结束时间戳、传输文件的名称(也包括整个路径)。似乎极不可能有两条记录具有相同的字段。

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

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

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

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

任何帮助是极大的赞赏。谢谢。

4

2 回答 2

3

使用串行列

您的计划是为 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 DISTINCTDISTINCT 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 版。它非常古老、过时且效率低下,不再受支持,并且可能有许多未修复的安全漏洞。官方 Postgres 版本控制站点。
@David已经提供了 SQL 语句。

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

于 2013-06-13T14:26:18.073 回答
2
  1. 您将无法使用重复项添加它。您需要先删除重复项。
  2. ALTER TABLE foo 添加约束 foo_pkey PRIMARY KEY(fld1, fld2);
  3. PostgreSQL会给你一个SQL状态代码的错误:23505。我对perl一无所知,但我假设你可以捕获这个。
  4. 再说一次,对 perl 一无所知,但我假设您可以捕获错误,然后由您决定如何处理它。

注意:您使用的是不受支持的 PostgreSQL 版本(您可能应该升级)。我什至无法在 SqlFiddle 上对此进行测试。因此,所有答案均基于 PosgreSQL 9.1

于 2013-06-13T14:13:47.420 回答