没有与ON CONFLICT匹配的唯一或排除约束

时间:2017-02-03 10:43:04

标签: sql postgresql postgresql-9.5

执行以下类型的插入时,我收到以下错误:

查询:

INSERT INTO accounts (type, person_id) VALUES ('PersonAccount', 1) ON
CONFLICT (type, person_id) WHERE type = 'PersonAccount' DO UPDATE SET
updated_at = EXCLUDED.updated_at RETURNING *

错误:

  

SQL执行失败(原因:错误:没有唯一或排除   约束匹配ON CONFLICT规范)

我也有一个独特的INDEX:

CREATE UNIQUE INDEX uniq_person_accounts ON accounts USING btree (type,
person_id) WHERE ((type)::text = 'PersonAccount'::text);

事情是有时候它有效,但不是每次都有效。我随机得到 那个例外,这真的很奇怪。它似乎无法访问它 INDEX或它不知道它存在。

有什么建议吗?

我正在使用PostgreSQL 9.5.5。

执行尝试查找或创建帐户的代码时的示例:

INSERT INTO accounts (type, person_id, created_at, updated_at) VALUES ('PersonAccount', 69559, '2017-02-03 12:09:27.259', '2017-02-03 12:09:27.259') ON CONFLICT (type, person_id) WHERE type = 'PersonAccount' DO UPDATE SET updated_at = EXCLUDED.updated_at RETURNING *
 SQL execution failed (Reason: ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification)

在这种情况下,我确定该帐户不存在。此外,当此人已经拥有帐户时,它永远不会输出错误。问题是,在某些情况下,如果还没有帐户,它也可以使用。查询完全相同。

4 个答案:

答案 0 :(得分:3)

这个错误的一个可能的简单解决方案

首先让我们通过一个简单的例子来看看错误原因。这是将产品映射到类别的表格。

create table if not exists product_categories (
    product_id uuid references products(product_id) not null,
    category_id uuid references categories(category_id) not null,
    whitelist boolean default false
);

如果我们使用这个查询:

INSERT INTO product_categories (product_id, category_id, whitelist)
VALUES ('123...', '456...', TRUE)
ON CONFLICT (product_id, category_id)
DO UPDATE SET whitelist=EXCLUDED.whitelist;

这会给您带来错误 No unique or exclusion constraint matching the ON CONFLICT,因为 product_idcategory_id 没有唯一约束。可能有多行具有相同的产品和类别 ID 组合。

解决方案:

像这样对 product_idcategory_id 使用唯一约束:

create table if not exists product_categories (
    product_id uuid references products(product_id) not null,
    category_id uuid references categories(category_id) not null,
    whitelist boolean default false,
    primary key(product_id, category_id) -- This will solve the problem
);

现在您可以对两列使用 ON CONFLICT (product_id, category_id) 而不会出现任何错误。

答案 1 :(得分:1)

我没有机会玩UPSERT,但我认为你有一个案例 docs

  

请注意,这意味着非部分唯一索引(唯一索引   将没有谓词)推断(因此由ON CONFLICT使用)   如果这样的指数满足所有其他标准是可用的。如果   尝试推理不成功,会引发错误。

答案 2 :(得分:1)

修复它的简单方法是将冲突列设置为 UNIQUE

答案 3 :(得分:0)

the docs

  

所有table_name唯一索引,无论顺序如何,都完全包含   冲突(target)指定的列/表达式被推断(选择)为仲裁器   索引。如果指定了index_predicate,则作为进一步要求,它必须   进行推理,请满足仲裁者索引。

文档继续说,

  

使用[index_predicate]来推断部分唯一索引

文档低调地表示,使用部分索引和 使用ON CONFLICT进行排序,必须指定index_predicate 。它不是 为你推断。我学到了 here,下面的示例对此进行了说明。

CREATE TABLE test.accounts (
    id int PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
    type text,
    person_id int);
CREATE UNIQUE INDEX accounts_note_idx on accounts (type, person_id) WHERE ((type)::text = 'PersonAccount'::text);
INSERT INTO  test.accounts (type, person_id) VALUES ('PersonAccount', 10);

这样我们就可以了:

unutbu=# select * from test.accounts;
+----+---------------+-----------+
| id |     type      | person_id |
+----+---------------+-----------+
|  1 | PersonAccount |        10 |
+----+---------------+-----------+
(1 row)

没有index_predicate,我们会收到错误消息:

INSERT INTO  test.accounts (type, person_id) VALUES ('PersonAccount', 10) ON CONFLICT (type, person_id) DO NOTHING;
-- ERROR:  there is no unique or exclusion constraint matching the ON CONFLICT specification

但是,如果您改为包含index_predicate WHERE ((type)::text = 'PersonAccount'::text)

INSERT INTO  test.accounts (type, person_id) VALUES ('PersonAccount', 10)
ON CONFLICT (type, person_id)
WHERE ((type)::text = 'PersonAccount'::text) DO NOTHING;

那么就没有错误,并且不做任何事情。