具有很多列的唯一索引

Unique index with a lots of columns

我有一个繁忙的 OLTP table,有 30 列和 5000 万行,我想避免其中出现重复。
我应该采取什么方法?

到目前为止我想到了这些:

对于后者,我觉得如果 table 架构发生变化,重新生成该哈希列会很麻烦。

也许还有其他一些我没有想到的方法?

Postgres 14

... 刚刚推出了 records 的内置哈希函数,这比我的自定义函数便宜得多。特别是对于很多列!参见:

  • Generate hash id for records in DB

这使得表达式索引比生成的列加索引更具吸引力。所以只是:

CREATE UNIQUE INDEX tbl_row_uni ON tbl (hash_record_extended(tbl.*,0));

这通常也有效:

CREATE UNIQUE INDEX tbl_row_uni ON tbl (hash_record_extended(tbl,0));

但第一个变体更安全。在第二个变体中,如果存在同名列,tbl 将解析为该列。

Postgres 13(原始答案)

我最近在 dba.SE 上提供了该问题的解决方案:

这与您的第三个想法非常接近:

基本上,一个非常有效的服务器端生成的散列被放置在具有 UNIQUE 约束的第 31 列。

CREATE OR REPLACE FUNCTION public.f_tbl_bighash(col1 text, col2 text, ... , col30 text)
  RETURNS bigint 
  LANGUAGE sql IMMUTABLE PARALLEL SAFE AS 
'SELECT hashtextextended(textin(record_out((,, ... ,))), 0)';

ALTER TABLE tbl
  ADD COLUMN tbl_bighash bigint NOT NULL GENERATED ALWAYS AS (public.f_tbl_bighash(col1, col2, ... , col30)) STORED  -- append column in last position
, ADD CONSTRAINT tbl_bighash_uni UNIQUE (tbl_bighash);

它的优点:无需更改任何其他内容即可高效运行。 (除非您在没有目标列表或类似内容的情况下使用 SELECT *INSERT INTO。)

它也适用于 NULL 个值(将它们视为相等)。

如果任何列类型具有非 immutable 文本表示,请小心。 (喜欢 timestamptz。)解决方案使用所有 text 列进行测试。

如果 table 架构更改 ,首先删除 UNIQUE 约束,重新创建函数并重新创建生成的列 - 理想情况下使用单个 ALTER TABLE 语句,所以你不会重写 table 两次。

或者,使用基于public.f_tbl_bighash()UNIQUE表达式索引。同样的效果。好处:没有额外的 table 列。缺点:在计算上有点贵。