【问题标题】:How do I reference a unique index that uses a function in ON CONFLICT?如何引用使用 ON CONFLICT 中的函数的唯一索引?
【发布时间】:2016-12-21 01:34:06
【问题描述】:

我使用的是 postgres 9.5.3,我有一个这样的表:

CREATE TABLE packages (
  id   SERIAL PRIMARY KEY,
  name VARCHAR NOT NULL
);

我已经定义了一个函数,canonical_name,像这样:

CREATE FUNCTION canonical_name(text) RETURNS text AS $$
    SELECT replace(lower($1), '-', '_')
$$ LANGUAGE SQL;

我已经为这个使用函数的表添加了一个唯一索引:

CREATE UNIQUE INDEX index_package_name
ON packages (canonical_name(name));
CREATE INDEX
# \d+ packages
                                                  Table "public.packages"
 Column |       Type        |                       Modifiers                       | Storage  | Stats target | Description
--------+-------------------+-------------------------------------------------------+----------+--------------+-------------
 id     | integer           | not null default nextval('packages_id_seq'::regclass) | plain    |              |
 name   | character varying | not null                                              | extended |              |
Indexes:
    "packages_pkey" PRIMARY KEY, btree (id)
    "index_package_name" UNIQUE, btree (canonical_name(name::text))

这个独特的索引正在按我的预期工作;它可以防止插入重复项:

INSERT INTO packages (name) 
VALUES ('Foo-bar');

INSERT INTO packages (name) 
VALUES ('foo_bar');

ERROR:  duplicate key value violates unique constraint "index_package_name"
DETAIL:  Key (canonical_name(name::text))=(foo_bar) already exists.

我的问题是我想使用这个唯一索引来做一个 upsert,我不知道我需要如何指定冲突目标。 The documentation 好像说我可以指定索引表达式:

where conflict_target can be one of:

    ( { index_column_name | ( index_expression ) } [ COLLATE collation ] [ opclass ] [, ...] ) [ WHERE index_predicate ]
    ON CONSTRAINT constraint_name

但是我尝试过的所有这些事情都会产生如图所示的错误,而不是正常工作的 upsert。

我已经尝试匹配我指定的索引表达式:

INSERT INTO packages (name)
VALUES ('foo_bar')
ON CONFLICT (canonical_name(name)) 
DO UPDATE SET name = EXCLUDED.name;

ERROR:  there is no unique or exclusion constraint matching the ON CONFLICT specification

匹配索引表达式为\d+显示它:

INSERT INTO packages (name)
VALUES ('foo_bar')
ON CONFLICT (canonical_name(name::text)) 
DO UPDATE SET name = EXCLUDED.name;


ERROR:  there is no unique or exclusion constraint matching the ON CONFLICT specification

只需命名唯一索引所在的列:

INSERT INTO packages (name)
VALUES ('foo_bar')
ON CONFLICT (name) 
DO UPDATE SET name = EXCLUDED.name;

ERROR:  there is no unique or exclusion constraint matching the ON CONFLICT specification

改用索引名称:

INSERT INTO packages (name)
VALUES ('foo_bar')
ON CONFLICT (index_package_name) 
DO UPDATE SET name = EXCLUDED.name;

ERROR:  column "index_package_name" does not exist
LINE 3: ON CONFLICT (index_package_name)

那么我如何指定我想使用这个索引呢?或者这是一个错误?

【问题讨论】:

    标签: postgresql upsert


    【解决方案1】:

    重要提示:这种行为只能在 9.5.4 之前的版本中观察到。这是 9.5.4 中修复的错误。答案的其余部分描述了错误的行为:

    如您所见,您只能为唯一的约束指定表达式,而不能为唯一的索引指定表达式。 这有点令人困惑,因为在幕后,唯一约束只是一个唯一索引(但这被认为是实现细节)。

    更糟糕的是,您不能在包含表达式的唯一索引上定义唯一约束——我不确定原因是什么,但怀疑是 SQL 标准。

    您可以这样做的一种方法是添加一个人工列,由触发器填充“规范名称”并定义该列的约束。我承认这样不好。

    然而,正确的解决方案是升级到 PostgreSQL 9.5 的最新次要版本。

    【讨论】:

    • 经过一些实验,(使用函数提升索引和检查约束)我认为人工列是唯一的解决方法。
    • 顺便说一句,不确定 9.5.3 但在 9.5.4 上,ON CONFLICT (canonical_name(name)) 变体工作正常。 PostgreSQL 9.5.4 on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2, 64-bit
    • @Abelisto: 该死的:我以为我在 9.5 #head 上进行了测试,但它似乎是 9.5.2 ...
    • Laurenz:感谢您的详细回答,您对 postgres
    • 我不明白,当前的文档 (postgresql.org/docs/current/static/sql-insert.html) 明确指出您可以为 constraint_name 使用多种选项,包括索引名称。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-22
    • 1970-01-01
    • 2014-09-08
    • 2023-02-05
    • 1970-01-01
    相关资源
    最近更新 更多