向表中插入数据。
语法
INSERT INTO [TABLE] [db.]table [(c1, c2, c3)] [SETTINGS ...] VALUES (v11, v12, v13), (v21, v22, v23), ...
你可以使用 (c1, c2, c3) 指定要插入的列列表。你也可以使用带有列匹配器 (如 *) 和/或修饰符 (如 APPLY、EXCEPT、REPLACE) 的表达式。
例如,假设有如下表:
SHOW CREATE insert_select_testtable;
CREATE TABLE insert_select_testtable
(
`a` Int8,
`b` String,
`c` Int8
)
ENGINE = MergeTree()
ORDER BY a
INSERT INTO insert_select_testtable (*) VALUES (1, 'a', 1) ;
如果你想将数据插入除列 b 之外的所有列,可以使用 EXCEPT 关键字。结合上面的语法,你需要确保插入的值数量 (VALUES (v11, v13)) 与指定的列数 ((c1, c3)) 一致:
INSERT INTO insert_select_testtable (* EXCEPT(b)) Values (2, 2);
SELECT * FROM insert_select_testtable;
┌─a─┬─b─┬─c─┐
│ 2 │ │ 2 │
└───┴───┴───┘
┌─a─┬─b─┬─c─┐
│ 1 │ a │ 1 │
└───┴───┴───┘
在此示例中,我们可以看到,第二个插入的行中,a 和 c 列使用传入的值填充,b 列则使用默认值填充。也可以使用 DEFAULT 关键字来插入默认值:
INSERT INTO insert_select_testtable VALUES (1, DEFAULT, 1) ;
如果列列表未包含所有现有列,其余列将使用以下内容填充:
- 根据表定义中指定的
DEFAULT 表达式计算得出的值。
- 如果未定义
DEFAULT 表达式,则填充为零和空字符串。
数据可以通过 ClickHouse 支持的任意一种 格式 传递给 INSERT。必须在查询中显式指定格式:
INSERT INTO [db.]table [(c1, c2, c3)] FORMAT format_name data_set
例如,以下查询格式与基础形式的 INSERT ... VALUES 完全相同:
INSERT INTO [db.]table [(c1, c2, c3)] FORMAT Values (v11, v12, v13), (v21, v22, v23), ...
ClickHouse 会移除数据前的所有空格,以及一个换行符 (如果存在) 。构造查询时,建议将数据放在查询运算符后的新一行中;如果数据以空格开头,这一点尤其重要。
示例:
INSERT INTO t FORMAT TabSeparated
11 Hello, world!
22 Qwerty
你可以使用命令行客户端或HTTP 接口,将数据与查询分开插入。
如果你想为 INSERT 查询指定 SETTINGS,则必须在 FORMAT 子句之前指定,因为 FORMAT format_name 之后的所有内容都会被视为数据。例如:INSERT INTO table SETTINGS ... FORMAT format_name data_set
如果表定义了约束,系统会检查插入数据中每一行是否满足相应的表达式。如果其中任何一项约束不满足,服务器将引发异常,其中包含约束名称和表达式,并停止该查询。
ClickHouse 仅在创建表 (CREATE TABLE) 和修改 schema (ALTER TABLE) 时验证允许使用的数据类型 (由 enable_time_time64_type、allow_suspicious_low_cardinality_types、allow_suspicious_fixed_string_types 等设置控制) ,不会在 INSERT 时进行验证。
这意味着,如果某个包含不允许数据类型的表已经存在,那么即使 server 上禁用了相应设置,仍然可以向其中插入数据。这是有意为之——表一旦创建完成,insert 操作就不应被控制类型创建的设置所阻止。
例如:
SET enable_time_time64_type = 1;
CREATE TABLE events
(
`id` UInt64,
`event_time` Time
)
ENGINE = MergeTree()
ORDER BY id;
SET enable_time_time64_type = 0;
-- 即使该设置现已禁用,此操作仍然有效。
-- 表已存在,因此插入操作不会被阻止。
INSERT INTO events VALUES (1, '14:30:25');
-- 但使用 Time 类型创建新表将会失败。
CREATE TABLE events_new
(
`id` UInt64,
`event_time` Time
)
ENGINE = MergeTree()
ORDER BY id; -- ERR: TYPE_TIME_TIME64_IS_NOT_ENABLED
因此,较新版本的客户端 (其中某项设置默认启用) 可以向较旧版本的服务器 (其中该设置已禁用) 插入包含不允许的数据类型的数据,前提是目标表已具有相应的列类型。校验是在 DDL 层面强制执行的,而不是在 DML 层面。
语法
INSERT INTO [TABLE] [db.]table [(c1, c2, c3)] SELECT ...
列会根据它们在 SELECT 子句中的位置进行映射。不过,它们在 SELECT 表达式中的名称与 INSERT 目标表中的名称可以不同。如有必要,会执行类型转换。
除 Values 格式外,其他任何数据格式都不允许将值设置为 now()、1 + 2 等表达式。Values 格式允许在有限范围内使用表达式,但不建议这样做,因为在这种情况下会使用低效的代码来执行这些表达式。
不支持其他修改数据分区片段的查询:UPDATE、DELETE、REPLACE、MERGE、UPSERT、INSERT UPDATE。
不过,你可以使用 ALTER TABLE ... DROP PARTITION 删除旧数据。
如果 SELECT 子句包含表函数 input(),则必须在查询末尾指定 FORMAT 子句。
若要向非 Nullable 数据类型的列中插入默认值而不是 NULL,请启用 insert_null_as_default 设置。
INSERT 也支持 CTE (公共表表达式) 。例如,以下两条语句是等价的:
INSERT INTO x WITH y AS (SELECT * FROM numbers(10)) SELECT * FROM y;
WITH y AS (SELECT * FROM numbers(10)) INSERT INTO x SELECT * FROM y;
语法
INSERT INTO [TABLE] [db.]table [(c1, c2, c3)] FROM INFILE file_name [COMPRESSION type] [SETTINGS ...] [FORMAT format_name]
使用上述语法可从存储在客户端侧的一个或多个文件中插入数据。file_name 和 type 是字符串字面量。输入文件的格式必须在 FORMAT 子句中设置。
支持压缩文件。压缩类型会根据文件名扩展名自动识别,也可以在 COMPRESSION 子句中显式指定。支持的类型包括:'none'、'gzip'、'deflate'、'br'、'xz'、'zstd'、'lz4'、'bz2'。
此功能可用于命令行客户端和 clickhouse-local。
示例
使用命令行客户端执行以下查询:
echo 1,A > input.csv ; echo 2,B >> input.csv
clickhouse-client --query="CREATE TABLE table_from_file (id UInt32, text String) ENGINE=MergeTree() ORDER BY id;"
clickhouse-client --query="INSERT INTO table_from_file FROM INFILE 'input.csv' FORMAT CSV;"
clickhouse-client --query="SELECT * FROM table_from_file FORMAT PrettyCompact;"
┌─id─┬─text─┐
│ 1 │ A │
│ 2 │ B │
└────┴──────┘
使用通配符通过 FROM INFILE 处理多个文件
此示例与前一个示例非常相似,但插入操作会通过 FROM INFILE 'input_*.csv' 从多个文件执行。
echo 1,A > input_1.csv ; echo 2,B > input_2.csv
clickhouse-client --query="CREATE TABLE infile_globs (id UInt32, text String) ENGINE=MergeTree() ORDER BY id;"
clickhouse-client --query="INSERT INTO infile_globs FROM INFILE 'input_*.csv' FORMAT CSV;"
clickhouse-client --query="SELECT * FROM infile_globs FORMAT PrettyCompact;"
除了使用 * 选择多个文件外,你还可以使用范围 ({1,2} 或 {1..9}) 以及其他通配符替换规则。以下三种写法都适用于上面的示例:INSERT INTO infile_globs FROM INFILE 'input_*.csv' FORMAT CSV;
INSERT INTO infile_globs FROM INFILE 'input_{1,2}.csv' FORMAT CSV;
INSERT INTO infile_globs FROM INFILE 'input_?.csv' FORMAT CSV;
可以向由表函数引用的表中插入数据。
语法
INSERT INTO [TABLE] FUNCTION table_func ...
示例
以下查询使用了 remote 表函数:
CREATE TABLE simple_table (id UInt32, text String) ENGINE=MergeTree() ORDER BY id;
INSERT INTO TABLE FUNCTION remote('localhost', default.simple_table)
VALUES (100, 'inserted via remote()');
SELECT * FROM simple_table;
┌──id─┬─text──────────────────┐
│ 100 │ inserted via remote() │
└─────┴───────────────────────┘
默认情况下,ClickHouse Cloud 中的服务会提供多个副本,以实现高可用性。当你连接到某个服务时,系统会与其中一个副本建立连接。
INSERT 成功后,数据会写入底层存储。不过,副本接收到这些更新可能需要一些时间。因此,如果你使用另一个连接,在其他副本上执行 SELECT 查询,更新后的数据可能还无法立即反映出来。
可以使用 select_sequential_consistency 强制副本接收最新更新。以下是一个使用此设置的 SELECT 查询示例:
SELECT .... SETTINGS select_sequential_consistency = 1;
请注意,使用 select_sequential_consistency 会增加 ClickHouse Keeper (ClickHouse Cloud 在内部使用的协调服务) 的负载,并且可能会因该服务的负载情况而导致性能下降。除非确有必要,我们不建议启用此设置。推荐的做法是在同一 session 中执行读写操作,或使用采用 native protocol 的客户端驱动 (因此支持粘性连接) 。
在复制环境中,数据完成复制后,才会在其他副本上可见。执行 INSERT 后,数据会立即开始复制 (即下载到其他副本) 。这与 ClickHouse Cloud 不同:在 ClickHouse Cloud 中,数据会立即写入共享存储,副本则通过订阅元数据变更进行同步。
请注意,在复制环境中,INSERTs 有时可能耗时较长 (大约为 1 秒) ,因为这需要提交到 ClickHouse Keeper 以达成分布式共识。使用 S3 作为存储也会带来额外延迟。
INSERT 会按主键对输入数据排序,并按分区键将其拆分到不同分区中。如果一次将数据插入多个分区,INSERT 查询的性能可能会明显下降。为避免这种情况:
- 以较大的批次写入数据,例如每次 100,000 行。
- 在将数据上传到 ClickHouse 之前,先按分区键对数据分组。
在以下情况下,性能通常不会下降:
对于小而频繁的插入操作,可以采用异步插入的方式。此类插入的数据会先合并为批次,然后再安全地写入表中。要使用异步插入,请启用 async_insert 设置。
使用 async_insert 或 Buffer 表引擎 都会带来额外的缓冲。
当插入大量数据时,ClickHouse 会通过称为“squashing”的过程来优化写入性能。内存中较小的已插入数据块会先合并并压缩成更大的块,然后再写入磁盘。squashing 可减少每次写入操作的相关开销。在此过程中,ClickHouse 每完成写入 max_insert_block_size 行后,这部分已插入的数据就可以被查询到。
另请参见