postgresql - 具有大量列的唯一索引

标签 postgresql indexing database-design unique-constraint

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

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

  • 30 列上的直接唯一索引(听起来像是一个巨大的缓慢索引)
  • 某种计算得出的 30 列总和的唯一索引
  • 应用程序端生成的哈希值放置在带有 btree 唯一索引的新第 31 列中

对于后者,我觉得如果表架构发生变化,重新生成该散列列将会有很多麻烦。

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

最佳答案

Postgres 14

...刚刚推出了一个用于记录的内置哈希函数,它比我的自定义函数便宜得多。尤其是对于很多专栏来说!请参阅:

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

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(($1,$2, ... ,$30))), 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 值(将它们视为相等)。

如果任何列类型具有不可变的文本表示形式,请小心。 (如 timestamptz。)该解决方案使用所有 text 列进行测试。

如果表架构发生更改,请先删除 UNIQUE 约束,重新创建函数并重新创建生成的列 - 最好使用单个 ALTER TABLE > 语句,这样您就不会重写该表两次。

或者,使用基于 public.f_tbl_bighash()UNIQUE 表达式索引。效果一样。优点:没有额外的表列。缺点:计算成本较高。

关于postgresql - 具有大量列的唯一索引,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69330676/

相关文章:

c - 在 C 中返回和打印字符串数组索引

google-app-engine - Google App Engine 索引成本

mysql - 在 MySQL 中的表之间创建一对一和一对多关系

sql - 在具有 MAX 聚合的 SQL 中使用 GROUP BY 并选择不在 GROUP BY 语句中的附加列

sql - postgreSQL - 计算虚拟表中的数量

mysql - 主索引和二级索引到底有什么区别?

mysql - 数据库中用户全名的典型长度应该是多少

mysql - 表本地化

postgresql - 如何在 Sequelize 中编写嵌套子查询

django - Heroku PostgreSQL Studio 找不到数据库