执行以下类型的插入时,我收到以下错误:
查询:
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)
在这种情况下,我确定该帐户不存在。此外,当此人已经拥有帐户时,它永远不会输出错误。问题是,在某些情况下,如果还没有帐户,它也可以使用。查询完全相同。
答案 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_id
和 category_id
没有唯一约束。可能有多行具有相同的产品和类别 ID 组合。
像这样对 product_id
和 category_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;
那么就没有错误,并且不做任何事情。