MySQL插入选择不强制执行NOT NULL约束

ada*_*dam 6 mysql constraints foreign-keys notnull insert-select

我遇到了MySQL 5.6 InnoDb在运行时忽略NOT NULL外键的问题INSERT INTO xxx (col) SELECT ....在以其他格式运行insert语句时,可以正确实施约束.启用外键检查,并且sql_mode = STRICT_TRANS_TABLES,STRICT_ALL_TABLES,NO_ENGINE_SUBSTITUTION

这是一个例子:

CREATE TABLE Test_Parent
(
    id BIGINT(18) UNSIGNED PRIMARY KEY NOT NULL AUTO_INCREMENT,
    dummy VARCHAR(255)
) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8 COLLATE = utf8_unicode_ci
    COMMENT 'Test parent table';

CREATE TABLE Test_Child
(
    id BIGINT(18) unsigned PRIMARY KEY NOT NULL AUTO_INCREMENT,
    fid BIGINT UNSIGNED NOT NULL,
    FOREIGN KEY Fk_Test_Parent_01(fid) REFERENCES Test_Parent(id)
) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8 COLLATE = utf8_unicode_ci
    COMMENT 'Test child table';

INSERT INTO Test_Parent(dummy)
VALUES ('test');

## Here's where the FK constraint should be enforced but isn't ##
INSERT INTO Test_Child(fid)
SELECT id
    FROM Test_Parent
WHERE dummy = 'missing value';

1 row affected in 5ms

## Running an insert with a different format, the constraint is enforced ##
INSERT INTO Test_Child(fid)
VALUES (null);
Column 'fid' cannot be null

## Running this format, the foreign key is also enforced ##
INSERT INTO Test_Child(id, fid)
VALUES (123, (SELECT id FROM Test_Parent WHERE dummy = 'missing value'));
Column 'fid' cannot be null
Run Code Online (Sandbox Code Playgroud)

我不明白为什么MySQL会为3个插入语句中的2个强制执行外键.有任何想法吗?

Mic*_*ski 1

来自您客户的误导性1 row affected in 5ms消息可能是造成混乱的根源。在评论线程中,您提到 IntelliJ 正在报告该消息,但我在 MySQL 5.6.35 和 5.7.16-ubuntu 中运行了您定义良好的测试表,并且在这两个版本中,相关语句报告了 0 个受影响的行:

mysql > INSERT INTO Test_Child(fid)
    -> SELECT id
    ->     FROM Test_Parent
    -> WHERE dummy = 'missing value';
Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0
Run Code Online (Sandbox Code Playgroud)

因此,纵观误导性的受影响行消息,这里真正发生的是语句SELECT的部分INSERT...SELECT与任何行都不匹配,因此 MySQL 不会尝试插入任何行。因此不存在外键约束违规。

格式INSERT INTO...SELECT与后面的示例略有不同:

INSERT INTO Test_Child(id, fid)
VALUES (123, (SELECT id FROM Test_Parent WHERE dummy = 'missing value'));
Run Code Online (Sandbox Code Playgroud)

...因为在这种情况下,数字文字123强制插入一行,并且它与null从子选择返回的值配对。因此null尝试插入并导致违反约束。

如果强制INSERT...SELECT返回具有空值的行,则可能会因违反约束而失败:

INSERT INTO Test_Child (fid)
-- Return a literal NULL row...
SELECT NULL as id FROM Test_Parent
-- Column fid cannot be null
Run Code Online (Sandbox Code Playgroud)