car*_*nts 13 postgresql upsert
我正在使用postgres 9.5.3,我有一个这样的表:
CREATE TABLE packages (
id SERIAL PRIMARY KEY,
name VARCHAR NOT NULL
);
Run Code Online (Sandbox Code Playgroud)
我已定义了一个函数canonical_name,如下所示:
CREATE FUNCTION canonical_name(text) RETURNS text AS $$
SELECT replace(lower($1), '-', '_')
$$ LANGUAGE SQL;
Run Code Online (Sandbox Code Playgroud)
我在这个表中添加了一个使用该函数的唯一索引:
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))
Run Code Online (Sandbox Code Playgroud)
这个独特的指数正如我所期望的那样工作; 它可以防止插入重复项:
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.
Run Code Online (Sandbox Code Playgroud)
我的问题是我想使用这个唯一索引进行upsert,我无法弄清楚我需要如何指定冲突目标.文档似乎说我可以指定一个索引表达式:
where conflict_target can be one of:
( { index_column_name | ( index_expression ) } [ COLLATE collation ] [ opclass ] [, ...] ) [ WHERE index_predicate ]
ON CONSTRAINT constraint_name
Run Code Online (Sandbox Code Playgroud)
但是我所尝试的所有这些事情都会产生如图所示的错误,而不是工作上的错误.
我已经尝试匹配索引表达式,因为我指定它:
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
Run Code Online (Sandbox Code Playgroud)
匹配索引表达式如下\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
Run Code Online (Sandbox Code Playgroud)
只需命名唯一索引所在的列:
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
Run Code Online (Sandbox Code Playgroud)
改为使用索引名称:
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)
Run Code Online (Sandbox Code Playgroud)
那么,如何做我指定我想用这个指标?或者这是一个错误?
不幸的是,你不能用PostgreSQL做到这一点.
如您所知,您只能为唯一约束指定表达式,而不能为唯一索引指定表达式.这有点令人困惑,因为在引擎盖下,唯一约束只是一个唯一索引(但这被认为是一个实现细节).
更糟糕的是,您无法在包含表达式的唯一索引上定义唯一约束 - 我不确定原因是什么,但怀疑SQL标准.
您可以这样做的一种方法是添加一个人工列,通过触发器填充"规范名称"并在该列上定义约束.我承认这不好.
或者,您可以在整个过程中使用可序列化的事务,并定义一个BEFORE触发器来验证您的状况.但这需要您处理代码中的序列化失败,这很麻烦.
| 归档时间: |
|
| 查看次数: |
3165 次 |
| 最近记录: |