当列设置为 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 中,NULL
和 NOT NULL
确实改变了数据类型的行为,但不是以其他关系数据库的方式 - 它在语法上与其他关系数据库兼容,但在语义上不兼容(a Int32 NULL
与 a Nullable(Int32)
相同,因为 a Int32 NOT NULL
与 a 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);
这样的语句将更有可能在大多数关系数据库中表现。
从 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 中,NULL
和 NOT NULL
确实改变了数据类型的行为,但不是以其他关系数据库的方式 - 它在语法上与其他关系数据库兼容,但在语义上不兼容(a Int32 NULL
与 a Nullable(Int32)
相同,因为 a Int32 NOT NULL
与 a 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);
这样的语句将更有可能在大多数关系数据库中表现。