当列值是 84 字节文本字段时,postgres 中保持列唯一的最有效方法是啥?

Posted

技术标签:

【中文标题】当列值是 84 字节文本字段时,postgres 中保持列唯一的最有效方法是啥?【英文标题】:Most efficient way in postgres to keep a column unique when that column value is an 84 byte text field?当列值是 84 字节文本字段时,postgres 中保持列唯一的最有效方法是什么? 【发布时间】:2020-07-25 20:39:21 【问题描述】:

计划使用哈希索引,但我还想有效地保持列的唯一性并在数据库中强制执行此操作。但是我真的不想构建一个 btree 并将我的数据库的大小加倍(整个表是一个文本字段、一个 bigint 和一个 int)。有没有办法编写约束以将哈希索引与数据字段的比较结合起来?我知道我不能在哈希索引上使用 UNIQUE 限定符,但这真的很好!有没有办法编写一个使用索引来获得唯一约束的约束,即使索引本身不支持它?

Postgres 版本 11+

http://rhaas.blogspot.com/2017/09/postgresqls-hash-indexes-are-now-cool.html

【问题讨论】:

确保唯一性的唯一方法是使用 btree 索引。如果你需要它,你将不得不忍受更大的索引大小。 【参考方案1】:

编辑添加(2021 年 10 月 28 日):

这可以通过排除约束来消除对主键列的需求:

# create table faux_unique (
  data text not null, 
  exclude using hash (data with =)
);
CREATE TABLE

# insert into faux_unique values('abc');
INSERT 0 1

# insert into faux_unique values('def');
INSERT 0 1

# insert into faux_unique values('abc');
ERROR:  conflicting key value violates exclusion constraint "faux_unique_data_excl"
DETAIL:  Key (data)=(abc) conflicts with existing key (data)=(abc).

# update faux_unique set data = 'abc' where data = 'def';
ERROR:  conflicting key value violates exclusion constraint "faux_unique_data_excl"
DETAIL:  Key (data)=(abc) conflicts with existing key (data)=(abc).

# select * from faux_unique;
┌──────┐
│ data │
├──────┤
│ abc  │
│ def  │
└──────┘
(2 rows)

原始答案,如下

您可以使用哈希索引来模拟带有check 约束的unique 约束,但您需要对表进行PK,否则该约束将无法捕获由于update 而导致的违规。

create table faux_unique (id serial primary key, data text not null);

create index faux_unique_hash_idx on faux_unique using hash (data);

create function is_unique_check(v_data text, v_id integer) returns boolean as $$
  select count(*) = 0
    from faux_unique
   where data = v_data
     and id != v_id;
$$ language sql;

alter table faux_unique add constraint faux_unique_check check (is_unique_check(data, id));

运行一些测试数据:

insert into faux_unique (data) values ('first'), ('second'), ('third');
INSERT 0 3

insert into faux_unique (data) values ('first');
ERROR:  new row for relation "faux_unique" violates check constraint "faux_unique_check"
DETAIL:  Failing row contains (4, first).

update faux_unique set data = 'second' where data = 'first';
ERROR:  new row for relation "faux_unique" violates check constraint "faux_unique_check"
DETAIL:  Failing row contains (1, second).

update faux_unique set data = 'second' where id = 1;
ERROR:  new row for relation "faux_unique" violates check constraint "faux_unique_check"
DETAIL:  Failing row contains (1, second).

insert into faux_unique (data) values ('fourth');
INSERT 0 1

【讨论】:

以上是关于当列值是 84 字节文本字段时,postgres 中保持列唯一的最有效方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

Hive : 当列值由分隔符 (~) 分隔时,将单个记录扩展为多个记录

当列值更改时,如何将组号添加到 SQL Server 2012 中的顺序记录?

ExtJS 4 - 当列编辑器是组合框时如何避免网格列值变为空?

使用带有自定义分隔符的 postgres 按字符大小复制文本文件

当列是 JSON 数组而不是字符串时如何过滤 ANTD 表

Postgres 默认值是两条记录的差