如果未在 NOT NULL 枚举列上指定值,如何使 MariaDB 产生错误?

Posted

技术标签:

【中文标题】如果未在 NOT NULL 枚举列上指定值,如何使 MariaDB 产生错误?【英文标题】:How to make MariaDB produce an error if value not specified on NOT NULL Enum Columns? 【发布时间】:2020-12-03 04:03:13 【问题描述】:

问题总结

对于 MariaDB,如果您未指定具有 NOT NULL 约束的列,则插入语句可能会产生错误。但是,如果该列的数据类型为 ENUM,则使用隐式默认值(列表中的第一个),并且插入不会产生错误。我想得到一个错误。

详情/我尝试了什么

数字数据类型错误

在带有 NOT NULL 约束的数字列的表上,我确实得到了预期的错误

create table test_number (
    name VARCHAR(40),
    num tinyint(1) not null
);

insert into test_number (name, num) values ('small',null); # sanity check
insert into test_number (name) values ('small');
#Response: Error Code: 1364. Field 'num' doesn't have a default value

枚举数据类型没有错误

在具有非空约束的 ENUM 列的表上,我没有收到错误

create table test_enum (
    name VARCHAR(40),
    size ENUM('x-small', 'small', 'medium', 'large', 'x-large') not null
);

insert into test_enum (name, size) values ('small',null); # sanity check
#Response: Error Code: 1048. Column 'size' cannot be null
insert into test_enum (name, size) values ('small',default(size)); # sanity check 2
#Response: Error Code: 1364. Field 'size' doesn't have a default value

但是下面的插入语句处理成功

insert into test_enum (name) values ('small');
select * from test_enum;
|#|name |size   |
|1|small|x-small|

如您所见,使用了隐式默认“x-small”,它是 create 语句列表中的第一个。

数据库配置

我在多个版本的 MariaDB >= 5.5 上尝试过,上面的输出是用 10.5.5 生成的。

SHOW VARIABLES LIKE '%version%';
|version|10.5.5-MariaDB-1:10.5.5+maria~focal|

我确实对数据库使用严格的 SQL_MODE

SELECT REPLACE(@@SQL_MODE, ',', '\n');
#|STRICT_ALL_TABLES|

期望的结果

每当我们添加一个枚举列而忘记在 DAO 层中调整相应的代码时,上述成功插入实际上就隐式发生在我们的应用程序中。 我想得到一个错误,这样 CI 中的现有测试就会失败。

【问题讨论】:

我通常以'unknown' 开始一个 ENUM 列表。它不会做你想做的事,但也不会混淆 x-small 和 small。 【参考方案1】:

欢迎来到论坛。

你不会喜欢我的解决方案,但我认为这可能是获得你想要的异常的唯一方法。

基于“如果一个 ENUM 列被声明为 NOT NULL,其默认值是允许值列表的第一个元素。”这一明显不可变的事实,那么您可能会通过 null 插入得到错误

一个。添加一个虚拟的 EMPTY STRING 值,例如"" 作为枚举字段中的第一个

b.定义触发器如下:

    CREATE TRIGGER `test_enum_BEFORE_INSERT` 
    BEFORE INSERT ON `test_enum` 
    FOR EACH ROW
    BEGIN
        IF new.size = "" THEN
            SIGNAL SQLSTATE '23000' SET mysql_ERRNO=1048, MESSAGE_TEXT='Column  "size" cannot be null';
        END IF;  
    END

枚举字段中的虚拟字符串实际上可以是任何东西,只要触发器测试它是什么。所以可以使用“Bad Value”,触发器的测试就变成了

IF new.size = "Bad Value" THEN...

SIGNAL 的触发将导致 INSERT 失败。

【讨论】:

【参考方案2】:

这是 documented behavior:

如果缺少DEFAULT 子句,则默认值为:

NULL 如果该列可以为空;

否则,枚举中的第一个值。

使用enums 时还有其他缺点,例如在维护值列表时。我实际上建议使用外键和引用表:这更灵活,更易于维护:

create table sizes (
    size varchar(20) primary key
);
insert into sizes values
    ('x-small'),
    ('small'),
    ('medium'),
    ('large'),
    ('x-large')
;

create table data (
    name varchar(40) primary key,
    size varchar(20) not null references sizes(size)
);

insert into data(name) values('foo');
-- error: Field 'size' doesn't have a default value

insert into data(name, size) values('foo', 'small');
-- ok

insert into data(name, size) values('bar', 'baz');
-- error: Cannot add or update a child row: a foreign key constraint fails (`db_2126492119`.`data`, CONSTRAINT `data_ibfk_1` FOREIGN KEY (`size`) REFERENCES `sizes` (`size`))

Demo on DB Fiddle

根据您的实际用例,您还可以使用自动递增的主键。

【讨论】:

以上是关于如果未在 NOT NULL 枚举列上指定值,如何使 MariaDB 产生错误?的主要内容,如果未能解决你的问题,请参考以下文章

如果最低版本和目标版本都设置为秋季创建者更新,为啥 Microsoft 模拟器未在 VS 中显示?

Microsoft.ACE.OLEDB.12.0' 提供程序未在本地计算机上注册“如果导入 (.xlsb) Excel 二进制格式文件

如果未在Chrome中定义变量,脚本会立即停止[重复]

Elmah 未在 MVC 应用程序中记录 http 发布请求的异常 - 如果请求包含 XML

VMware该虚拟机似乎正在使用中。如果该虚拟机未在使用,请按“获取所有权(T)”按钮获取它的所有权

如果一对列值未在另一个 df2 中配对,则删除 df1 中的行