UPSERT功能
功能描述
UPSERT用于向目标表中插入或更新数据,该子句中指定了INSERT时的冲突键,以及冲突后的处理方式:当插入值违背了唯一约束时,则更新数据,不冲突则直接插入数据。
因此被称为UPSERT功能——“UPDATE 或 INSERT”。在没有无关错误的前提下,此功能保证了一个INSERT或者UPDATE结果的原子性。
注意事项
ON CONFLICT语法暂不支持在视图、外部表、分区表中使用。
conflict_target用于指定冲突检测的目标,该目标必须能关联到唯一索引。目前暂不支持不指定conflict_target的用法。
INSERT ON DUPLICATE KEY UPDATE不支持WITH及WITH RECURSIVE子句。
支持insert的数据类型允许在SQL的insert部分写入。
支持update的数据类型允许在upsert_clause中的update子句中更新。
upsert语句暂不支持以下场景:
- SQL中包含 upsert子句和 returning子句。
- SQL中包含upsert子句和 with子句。
- update set子句中包含子查询。
- upsert子句的conflict_target为空。
- upsert子句的conflict_target为ON CONSTRAINT name。
- upsert子句的conflict_target中包含where子句。
- upsert子句的conflict_action中包含where子句。
- upsert子句的conflict_target指定了ASC/DESC 、NULLS FIRSTLAST关键字。
- upsert语法不支持在分区表、视图、外部表中使用。
语法格式
INSERT语法:
[ WITH [ RECURSIVE ] with_query [, ...] ]
INSERT [/*+ plan_hint */] [IGNORE] INTO table_name [ @dblink_name ] [partition_clause] [ AS alias ] [ ( column_name [, ...] ) ]
{ DEFAULT VALUES | VALUES | VALUE {( { expression | DEFAULT } [, ...] ) }[, ...] | query }
[ upsert_clause ]
[ RETURNING {* | {output_expression [ [ AS ] output_name ] }[, ...]} ];
INSERT [ FIRST | ALL ]
[ WHEN { condition } THEN INTO table [ [ AS ] alias ] [ ( column_name [, ...] ) ] VALUES (v1, ...), ...]
{ subquery }
Vastbase支持两种风格的upsert_clause语法:
ON DUPLICATE KEY UPDATE { NOTHING | { column_name = { expression | DEFAULT } } [, ...] }
ON CONFLICT [ conflict_target ] conflict_action
冲突检测子句conflict_target可以是:
( { index_column_name | ( index_expression ) } [ COLLATE collation ] [ opclass ] [, ...] ) [ WHERE index_predicate ] ON CONSTRAINT constraint_name
冲突处理子句conflict_action可以是:
DO NOTHING DO UPDATE SET { column_name = { expression | DEFAULT } | ( column_name [, ...] ) = [ ROW ] ( { expression | DEFAULT } [, ...] ) | ( column_name [, ...] ) = ( sub-SELECT ) } [, ...] [ WHERE condition ]
参数说明
ON DUPLICATE KEY UPDATE
NOTHING
ON DUPLICATE KEY UPDATE表示检测到冲突时什么也不做,忽略此条插入。
column_name = { expression | DEFAULT }
此表达式指定了冲突发生后的UPDATE动作。
expression
表达式expression支持使用子查询表达式,其语法与功能同UPDATE。子查询表达式中支持使用
EXCLUDED.
来选择源数据相应的列。DEFAULT
相应的列将填充其默认值。标识列将由关联序列生成的新值填充。
对于生成的列,允许指定该值,但仅指定根据其生成表达式计算该列的正常行为。
ON CONFLICT DO UPDATE
conflict_target
通过选择判断目标索引来指定哪些行与ON CONFLICT在其上采取可替代动作的行相冲突。要么执行唯一索引推断,要么显式命名一个约束。
除非指定了ON CONFLICT DO NOTHING,否则必须提供conflict_target。
( { index_column_name | ( index_expression ) } [, …] ) [ WHERE index_predicate ]
conflict_target可以执行唯一索引推断。在执行推断时,它由一个或者多个 index_column_name 列或者 index_expression 表达式以及一个可选的 index_predicate构成。所有刚好包含 conflict_target指定的列/表达式的table_name唯一索引(不管顺序)都会被推断为(选择为)仲裁者索引。如果指定了 index_predicate,它必须满足仲裁者索引(也是推断过程的一个进一步的要求)。注意这意味着如果有一个满足其他条件的非部分唯一索引(没有谓词的唯一索引)可用,它将被推断为仲裁者(并且会被ON CONFLICT使用)。如果推断尝试不成功,则会抛出一个错误。
index_column_name
table_name中一个列的名称。它被用来检测对象是否冲突。
index_expression
和index_column_name类似,但是被用来判断出现在索引定义中的table_name列(非简单列)上的表达式。
Collation
强制相应的index_column_name或index_expression使用一种特定的排序规则以便在检测冲突期间能被匹配上。默认为缺省值。排序规则不影响冲突检测,一般不指定。
opclass
强制相应的index_column_name或index_expression使用特定的操作符类以便在检测冲突期间能被匹配上。通常会被省略。
index_predicate
用于允许判断部分唯一索引。任何满足该谓词(不一定需要真的是部分索引)的索引都能被推断。
constraint_name
用名称显式地指定一个判断目标对象约束。本版本中暂不支持。
conflict_action
conflict_action指定一个可替换的ON CONFLICT动作。它可以是DO NOTHING,也可以是一个指定在冲突情况下要被执行的UPDATE动作细节的DO UPDATE子句。ON CONFLICT DO UPDATE中的SET和WHERE子句能够使用该表的名称(或者别名)访问现有的行,并且可以用特殊的excluded表访问要插入的行。注意,不支持引用系统列例如ctid作为更新值,当前版本暂时不支持sub-SELECT做更新。
DO NOTHING
检测到冲突时什么也不做。
DO UPDATE SET
检测到冲突时执行SET子句指定的动作。
在SET子句中,可用
EXCLUDED.
来引用要插入的列值,但不支持引用系统列(比如ctid)。例如:UPDATE SET c1=EXCLUDE.c1
表示将c1更新为SQL中insert部分计划插入的c1字段的值;不支持UPDATE setc1=EXCLUDE.ctid:text
的用法。
{ column_name = { expression | DEFAULT }
column_name
table_name表中的一个列的名称。如有必要,列名可以用一个子域名或者数组下标限定(指向一个组合列的某些列中插入会让其他域为空)。
expression
要赋予给相应列的表达式或者值。
DEFAULT
相应的列将填充其默认值。标识列将由关联序列生成的新值填充。对于生成的列,允许指定该值,但仅指定根据其生成表达式计算该列的正常行为。
[ WHERE condition ]
当前版本暂不支持WHERE条件表达式。
示例
ON DUPLICATE KEY UPDATE
1、创建测试表并插入数据。
CREATE TABLE test01 (col1 INT PRIMARY KEY, col2 INT);
insert into test01 values(1,1);
2、根据col1字段值判断数据库中是否有记录'1',有则更新字段col2为'2',没有则插入col1为'1'。
INSERT INTO test01(col1) VALUES(1) ON DUPLICATE KEY UPDATE col2 = 2;
3、验证上一步的操作结果。
SELECT * FROM test01;
当结果显示为如下信息,表示col2的数据被更新功。
col1 | col2
------+------
1 | 2
(1 row)
ON CONFLICT DO UPDATE
1、创建测试表,在字段c1上创建唯一索引。
create table tu1(c1 int unique, c2 int);
2、执行如下语句,若c1值不冲突,则插入一条数据(3,3);若c1值冲突,则将c2更新为SQL中insert部分计划插入的c1字段的值。
insert into tu1 values(3,3) on conflict(c1) do update set c2=EXCLUDED.c2;
3、插入成功,查询测试表数据。
select * from tu1;
返回结果如下:
c1 | c2
----+----
3 | 3
(1 row)
4、执行如下语句,c1值发生冲突,将c2更新为SQL中insert部分计划插入的c2字段的值。
insert into tu1 values(3,4) on conflict(c1) do update set c2=EXCLUDED.c2;
5、查询测试表数据。
select * from tu1;
冲突更新成功,返回结果如下:
c1 | c2
----+----
3 | 4
(1 row)
6、执行如下语句,如c1值冲突,将什么都不做。
insert into tu1 values(3,5) on conflict(c1) do nothing;
7、查看测试表数据。
select * from tu1;
返回结果如下,表示没有执行任何操作:
c1 | c2
----+----
3 | 4
(1 row)