当需要具有运算符类的索引时,唯一索引是否比唯一约束更好

use*_*760 2 postgresql index database-design unique-constraint

Postgres 文档说:

使用索引来强制唯一约束可以被视为不应直接访问的实现细节。但是,应该注意没有必要在唯一的列上手动创建索引;这样做只会复制自动创建的索引。

基于此,如果我想要一个列上的表达式索引并且还希望该列是唯一的,那么case 2下面会更好,因为它可以使用单个索引完成上述操作。而case 1由于唯一约束而自动创建一个索引,而另一个因为我需要小写索引而自动创建?

正如@Colin'tHart 指出的那样,这两种情况不一样。我应该在不使用lower()表达式的情况下发布这个问题。在那种情况下,我的理解是 aCREATE UNIQUE INDEX比唯一约束和简单索引更好。

基于此,如果我想要text_pattern_ops在列上使用运算符类(例如)的索引并且还希望该列是唯一的,那么case 2下面会更好,因为它可以使用单个索引完成上述操作。而case 1会因为唯一约束而自动创建一个索引,而另一个因为我需要不同的运算符类而自动创建?

情况1:

CREATE TABLE book (
   id SERIAL PRIMARY KEY,
   name text NOT NULL,
   CONSTRAINT book_name_key UNIQUE (name)
);

CREATE INDEX book_name_like ON book (name text_pattern_ops);
Run Code Online (Sandbox Code Playgroud)

案例2:

CREATE TABLE book (
   id SERIAL PRIMARY KEY,
   name text NOT NULL
);

CREATE UNIQUE INDEX book_name_like ON book (name text_pattern_ops);
Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 6

有那么一刻我想一个可能能够使用预先存在的 text_pattern_ops指数与USING INDEX增加一个条款时UNIQUE CONSTRAINT。但这失败了,因为:

ERROR: index "book2_name_like" does not have default sorting behavior
Run Code Online (Sandbox Code Playgroud)

手册:

索引不能有表达式列,也不能是部分索引。此外,它必须是具有默认排序顺序的 b 树索引。这些限制确保索引等同于由常规ADD PRIMARY KEYADD UNIQUE命令构建的索引。

例如,像这样的唯一索引将允许 FK 约束引用它,但性能很差,因为它不支持标准运算符。
手册:

请注意,你也应该,如果你想涉及普通查询创建的缺省操作符的索引<<=>,或>=比较使用索引。

所以要回答这个问题:

如果需要一个UNIQUE CONSTRAINT(除其他原因外:用 FK 引用它),您的第一个带有约束和索引的变体是唯一的选择。此外,约束创建的索引的默认运算符类支持更多操作(如按默认排序顺序排序)。

如果您不需要任何这些,请使用您的第二个变体,因为很明显,只有一个索引的维护成本更低:只是一个UNIQUE text_pattern_ops索引。

索引和约束的区别:

用 COLLATE "C" 替代

除了创建两个索引之外,还有另一种可能更可取的索引替代方法。手册:xxx_pattern_ops

与默认操作符类的不同之处在于,这些值严格逐个字符地进行比较,而不是根据特定于语言环境的排序规则进行比较。LIKE当数据库不使用标准的“C”语言环境时,这使得这些运算符类适用于涉及模式匹配表达式(或 POSIX 正则表达式)的查询。

和:

索引自动使用基础列的排序规则。

您可以创建没有排序规则的列(使用COLLATE "C")。然后默认运算符类的行为方式与text_pattern_ops将相同- 加上索引可以与所有标准运算符一起使用。

CREATE TABLE book2 (
   book_id serial PRIMARY KEY,
   book    text NOT NULL COLLATE "C" UNIQUE  -- that's all!
);
Run Code Online (Sandbox Code Playgroud)

现在,LIKE可以使用索引:

SELECT * FROM book2 WHERE book LIKE 'foo%';
Run Code Online (Sandbox Code Playgroud)

ILIKE仍然不能:

SELECT * FROM book2 WHERE book ILIKE 'foo%';
Run Code Online (Sandbox Code Playgroud)

db<>在这里 摆弄旧的sqlfddle

考虑使用附加模块 pg_trgm 的三元索引以获得更通用的解决方案: