INSERT
功能描述
向表中添加一行或多行数据。
注意事项
- 只有拥有表INSERT权限的用户,才可以向表中插入数据。用户被授予insert any table权限,相当于用户对除系统模式之外的任何模式具有USAGE权限,并且拥有这些模式下表的INSERT权限
- 如果使用RETURNING子句,用户必须要有该表的SELECT权限。
- 如果使用ON DUPLICATE KEY UPDATE,用户必须要有该表的SELECT、UPDATE权限,唯一约束(主键或唯一索引)的SELECT权限。
- 如果使用query子句插入来自查询里的数据行,用户还需要拥有在查询里使用的表的SELECT权限。
- 生成列不能被直接写入。在INSERT命令中不能为生成列指定值,但是可以指定关键字DEFAULT。
- 列存表不支持RETURNING子句。
当连接到TD兼容模式的数据库,td_compatible_truncation参数设置为on时,将启用超长字符串自动截断功能,在后续的insert语句中(不包含外表的场景下),对目标表中char和varchar类型的列上插入超长字符串时,系统会自动按照目标表中相应列定义的最大长度对超长字符串进行截断。
如果向字符集为字节类型编码(SQL_ASCII,LATIN1等)的数据库中插入多字节字符数据(如汉字等),且字符数据跨越截断位置,这种情况下,按照字节长度自动截断,自动截断后会在尾部产生非预期结果。如果用户有对于截断结果正确性的要求,建议用户采用UTF8等能够按照字符截断的输入字符集作为数据库的编码集。
优化建议: 通过insert语句批量插入数据时,建议将多条记录合并入一条语句中执行插入,以提高数据加载性能。例如:
INSERT INTO sections VALUES (30, 'Administration', 31, 1900),(40, 'Development', 35, 2000), (50, 'Development' , 60 , 2001);
语法格式
[ WITH [ RECURSIVE ] with_query [, ...] ]
INSERT [/*+ plan_hint */] INTO table_name [ @dblink_name ] [partition_clause] [ AS alias ] [ ( column_name [, ...] ) ]
{ DEFAULT VALUES | VALUES {( { expression | DEFAULT } [, ...] ) }[, ...] | query }
[ upsert_clause ]
[ RETURNING {* | {output_expression [ [ AS ] output_name ] }[, ...]} ];
INSERT [ First | All ]
[ WHEN { Condition } THEN INTO table [ [ AS ] alias ] Values (v1, ...), ...]
{ Subquery }
其中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 c ollation ] [ 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 ]
其中with_query可以是:
with_query_name [ ( column_name [, ...] ) ] AS [ [ NOT ] MATERIALIZED ]( {select | values | insert | update | delete} )
其中partition_clause可以是:
PARTITION { ( partition_name ) | FOR ( partition_value [, ...] ) } | SUBPARTITION { ( subpartition_name ) | FOR ( subpartition_value [, ...] ) }
其中partition_clause仅在集中式模式支持。
参数说明
WITH [ RECURSIVE ] with_query [, …]
用于声明一个或多个可以在主查询中通过名称引用的子查询,相当于临时表。
如果声明了RECURSIVE,那么允许SELECT子查询通过名称引用它自己。
with_query_name指定子查询生成的结果集名称,在查询中可使用该名称访问子查询的结果集。
column_name指定子查询结果集中显示的列名。
每个子查询可以是SELECT,VALUES,INSERT,UPDATE或DELETE语句。
用户可以使用MATERIALIZED / NOT MATERIALIZED对CTE(Common Table Expression,即在SQL中构建一个临时数据集,给SQL的其他部分语义使用)进行修饰。
- 如果声明为MATERIALIZED,WITH查询将被物化,生成一个子查询结果集的拷贝,在引用处直接查询该拷贝,因此WITH子查询无法和主干SELECT语句进行联合优化(如谓词下推、等价类传递等),对于此类场景可以使用NOT MATERIALIZED进行修饰,如果WITH查询语义上可以作为子查询内联执行,则可以进行上述优化。
- 如果用户没有显示声明物化属性则遵守以下规则:如果CTE只在所属主干语句中被引用一次,且语义上支持内联执行,则会被改写为子查询内联执行,否则以CTE Scan的方式物化执行。
INSERT ON DUPLICATE KEY UPDATE不支持WITH及WITH RECURSIVE子句。
plan_hint子句
以/*+ */的形式在INSERT关键字后,用于对INSERT对应的语句块生成的计划进行hint调优,详细用法请参见章节使用Plan Hint进行调优。每条语句中只有第一个
/*+ plan_hint */
注释块会作为hint生效,里面可以写多条hint。table_name
要插入数据的目标表名。
取值范围:已存在的表名。
alias
目标表的别名。
取值范围:字符串,符合标识符命名规范。
dblink_name
远程连接名称。
partition_clause
指定分区插入操作。
partition_name
分区名。
partition_value
指定的分区键值。在创建分区表时,如果指定了多个分区键,可以通过PARTITION FOR子句指定的这一组分区键的值,唯一确定一个分区。 如果value子句的值和指定分区不一致,会抛出异常。
subpartition_name
子分区名。
subpartition_value
指定的子分区键值。 如果value子句的值和指定子分区不一致,会抛出异常。
示例参见CREATE TABLE SUBPARTITION的示例。
column_name
目标表中的字段名:
- 字段名可以有子字段名或者数组下标修饰。
- 没有在字段列表中出现的每个字段,将由系统默认值,或者声明时的默认值填充,若都没有则用NULL填充。例如,向一个复合类型中的某些字段插入数据的话,其他字段将是NULL。
- 目标字段(column_name)可以按顺序排列。如果没有列出任何字段,则默认全部字段,且顺序为表声明时的顺序。
- 如果value子句和query中只提供了N个字段,则目标字段为前N个字段。
- value子句和query提供的值在表中从左到右关联到对应列。
取值范围:已存在的字段名。
expression
赋予对应column的一个有效表达式或值:
如果是INSERT ON DUPLICATE KEY UPDATE语句下,expression可以为
VALUES(column_name)
或EXCLUDED.column_name
用来表示引用冲突行对应的column_name字段的值。需注意,其中VALUES(column_name)不支持嵌套在表达式中(例如VALUES(column_name)+1),但EXCLUDED不受此限制。向表中字段插入单引号
'
时需要使用单引号自身进行转义。如果插入行的表达式不是正确的数据类型,系统试图进行类型转换,若转换不成功,则插入数据失败,系统返回错误信息。
DEFAULT
对应字段名的缺省值。如果没有缺省值,则为NULL。
query
一个查询语句(SELECT语句),将查询结果作为插入的数据。
RETURNING
返回实际插入的行,RETURNING列表的语法与SELECT的输出列表一致。
INSERT ON DUPLICATE KEY UPDATE不支持RETURNING子句。
output_expression
INSERT命令在每一行都被插入之后用于计算输出结果的表达式。
取值范围:该表达式可以使用table的任意字段。可以使用*返回被插入行的所有字段。
output_name
字段的输出名称。
取值范围:字符串,符合标识符命名规范。
First | All
first表示考虑先后关系,在执行第一个满足条件的插入语句后就会结束。
All表示所有满足条件的插入语句都会被执行,并且INSERT All支持针对指定字段批量插入数据。
ON DUPLICATE KEY UPDATE
对于带有唯一约束(UNIQUE INDEX或PRIMARY KEY)的表,如果插入数据违反唯一约束,则对冲突行执行UPDATE子句完成更新,对于不带唯一约束的表,则仅执行插入。UPDATE时,若指定NOTHING则忽略此条插入,可通过
EXCLUDE.
或者VALUES()
来选择源数据相应的列。支持触发器,触发器执行顺序由实际执行流程决定:
- 执行insert:触发 before insert、after insert触发器。
- 执行update:触发before insert、before update、after update触发器。
- 执行update nothing:触发before insert触发器。
不支持延迟生效(DEFERRABLE)的唯一约束或主键。
如果表中存在多个唯一约束,如果所插入数据违反多个唯一约束,对于检测到冲突的第一行进行更新,其他冲突行不更新(检查顺序与索引维护具有强相关性,一般先创建的索引先进行冲突检查)。
如果插入多行,这些行均与表中同一行数据存在唯一约束冲突,则按照顺序,第一条执行插入或更新,之后依次执行更新。
主键、唯一索引列不允许UPDATE。
不支持列存,不支持外表、内存表。
expression支持使用子查询表达式,其语法与功能同UPDATE。子查询表达式中支持使用
EXCLUDED.
来选择源数据相应的列。
( { 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条件表达式,可选。
用于指定UPDATE的条件。在INSERT语句遇到冲突后,满足WHERE条件时数据会被更新,不满足条件时不执行。
with_query_name
指定子查询生成的结果集名称,在查询中可使用该名称访问子查询的结果集。
column_name
指定子查询结果集中显示的列名。
{select | values | insert | update | delete}
表示每个子查询可以是SELECT,VALUES,INSERT,UPDATE或DELETE语句。
示例
1、创建表reason_t2。
CREATE TABLE reason_t2 ( r_reason_sk integer, r_reason_id character(16), r_reason_desc character(100) );
2、向表中插入一条记录并查询。
INSERT INTO reason_t2(r_reason_sk, r_reason_id, r_reason_desc) VALUES (1, 'AAAAAAAABAAAAAAA', 'reason1'); SELECT * FROM reason_t2;
查询结果为:
r_reason_sk | r_reason_id | r_reason_desc -------------+------------------+------------------------------------------------------------------------------------------------------ 1 | AAAAAAAABAAAAAAA | reason1 (1 row)
2、向表中插入一条记录并查询,和上一条语法等效。
INSERT INTO reason_t2 VALUES (2, 'AAAAAAAABAAAAAAA', 'reason2'); SELECT * FROM reason_t2;
查询结果为:
r_reason_sk | r_reason_id | r_reason_desc -------------+------------------+------------------------------------------------------------------------------------------------------ 1 | AAAAAAAABAAAAAAA | reason1 2 | AAAAAAAABAAAAAAA | reason2 (2 rows)
3、向表中插入多条记录并查询。
INSERT INTO reason_t2 VALUES (3, 'AAAAAAAACAAAAAAA','reason3'),(4, 'AAAAAAAADAAAAAAA', 'reason4'),(5, 'AAAAAAAAEAAAAAAA','reason5'); SELECT * FROM reason_t2;
查询结果为:
r_reason_sk | r_reason_id | r_reason_desc -------------+------------------+------------------------------------------------------------------------------------------------------ 1 | AAAAAAAABAAAAAAA | reason1 2 | AAAAAAAABAAAAAAA | reason2 3 | AAAAAAAACAAAAAAA | reason3 4 | AAAAAAAADAAAAAAA | reason4 5 | AAAAAAAAEAAAAAAA | reason5 (5 rows)
4、向表中插入reason中r_reason_sk小于5的记录并查询。
INSERT INTO reason_t2 SELECT * FROM reason_t2 WHERE r_reason_sk <5; SELECT * FROM reason_t2;
查询结果为:
r_reason_sk | r_reason_id | r_reason_desc -------------+------------------+------------------------------------------------------------------------------------------------------ 1 | AAAAAAAABAAAAAAA | reason1 2 | AAAAAAAABAAAAAAA | reason2 3 | AAAAAAAACAAAAAAA | reason3 4 | AAAAAAAADAAAAAAA | reason4 5 | AAAAAAAAEAAAAAAA | reason5 1 | AAAAAAAABAAAAAAA | reason1 2 | AAAAAAAABAAAAAAA | reason2 3 | AAAAAAAACAAAAAAA | reason3 4 | AAAAAAAADAAAAAAA | reason4 (9 rows)
5、对表创建索引。
CREATE INDEX reason_t2_u_index ON reason_t2(r_reason_sk);
6、向表中插入多条记录,如果冲突则更新冲突数据行中r_reason_id字段为'BBBBBBBBCAAAAAAA',并查询。
INSERT INTO reason_t2 VALUES (5, 'BBBBBBBBCAAAAAAA','reason5'),(6, 'AAAAAAAADAAAAAAA', 'reason6') ON DUPLICATE KEY UPDATE r_reason_id = 'BBBBBBBBCAAAAAAA'; SELECT * FROM reason_t2;
查询结果为:
r_reason_sk | r_reason_id | r_reason_desc -------------+------------------+------------------------------------------------------------------------------------------------------ 1 | AAAAAAAABAAAAAAA | reason1 2 | AAAAAAAABAAAAAAA | reason2 3 | AAAAAAAACAAAAAAA | reason3 4 | AAAAAAAADAAAAAAA | reason4 5 | AAAAAAAAEAAAAAAA | reason5 1 | AAAAAAAABAAAAAAA | reason1 2 | AAAAAAAABAAAAAAA | reason2 3 | AAAAAAAACAAAAAAA | reason3 4 | AAAAAAAADAAAAAAA | reason4 5 | BBBBBBBBCAAAAAAA | reason5 6 | AAAAAAAADAAAAAAA | reason6 (11 rows)
7、删除表reason_t2。
DROP TABLE reason_t2;