当列设置为 NOT NULL 时如何拒绝 INSERT

How can I decline an INSERT when column is set to NOT NULL

documentation 开始,您必须在列定义中放置一个 NOT NULL 修饰符来标记它,就像其他 SQL 数据库一样。

考虑这个 table:

CREATE TABLE test (
    name String NOT NULL,
    isodate DateTime('Europe/Berlin') NOT NULL
) ENGINE = MergeTree()
ORDER BY (isodate)

如果我尝试为两列(或至少一列)插入 NULL,预期的行为是 Clickhouse 拒绝插入,因为列被标记为 NOT NULL。相反,Clickhouse 创建了一个新行,其中 isodate 是 1970-01-01 01:00:00 并且 name 是一个空字符串,这显然是这些数据类型的默认值。

我必须怎么做才能让 Clickhouse 拒绝此类插入?

我的Clickhouse服务器版本是21.12.3。

具有 Not Null 约束的 Clickhouse 行为与其他数据库不兼容。

您可以使用检查约束来克服它https://clickhouse.com/docs/en/sql-reference/statements/create/table/#constraints

CREATE TABLE test (
    name String NOT NULL,
    isodate DateTime('Europe/Berlin') NOT NULL,
    CONSTRAINT isodate_not_null CHECK isodate <> toDateTime(0, 'Europe/Berlin')
) ENGINE = MergeTree()
ORDER BY (isodate)


insert into test(name) values ('x');
DB::Exception: Constraint `isodate_not_null` for table default.test (f589312a-1592-426a-b589-312a1592b26a) is violated at row 1. Expression: (isodate != toDateTime(0)). Column values: isodate = 0. (VIOLATED_CONSTRAINT)


insert into test values ('x', now());
OK.

原因是性能,在 OLAP 数据库中需要尽可能快地摄取数据。

在 ClickHouse 中,NULLNOT NULL 确实改变了数据类型的行为,但不是以其他关系数据库的方式 - 它在语法上与其他关系数据库兼容,但在语义上不兼容(a Int32 NULLa Nullable(Int32) 相同,因为 a Int32 NOT NULLa Int32 相同)。定义为 NOT NULL 的列并不意味着它将拒绝在插入语句中插入值为 NULL 的字段——这意味着 ClickHouse 将使用列类型的默认表达式(或者如果未指定在列定义中,数据类型的默认值)。当启用 input_format_null_as_default(Clickhouse 21.12.3 的默认设置)时,ClickHouse 会出现这种行为。

要为此类无效值抛出异常,您需要将系统设置 input_format_null_as_default 更改为 0。如果您使用 clickhouse-client,您可以在连接到 clickhouse 时禁用它:

clickhouse-client -h ... --input_format_null_as_default 0

或之后:

clickhouse> SET input_format_null_as_default=0

这样,像 insert into test (name, isodate) values (NULL, NULL); 这样的语句将更有可能在大多数关系数据库中表现。