MySQL 插入 Select 不强制执行 NOT NULL 约束
MySQL Insert Select Doesn't Enforce NOT NULL Constraint
我遇到了 MySQL 5.6 InnoDb 在 运行 和 INSERT INTO xxx (col) SELECT ...
时忽略 NOT NULL 外键的问题。当 运行 插入其他格式的语句时,约束会被正确执行。外键检查已启用,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
我不明白为什么 MySQL 会对 3 个插入语句中的 2 个强制执行外键。有什么想法吗?
来自您客户的误导性 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
所以看看误导性的受影响的行消息,这里真正发生的是 INSERT...SELECT
语句的 SELECT
部分不匹配任何行,因此 MySQL 不会产生尝试插入任何行。因此没有违反外键约束。
INSERT INTO...SELECT
的格式与您后面的示例略有不同:
INSERT INTO Test_Child(id, fid)
VALUES (123, (SELECT id FROM Test_Parent WHERE dummy = 'missing value'));
...因为在这种情况下,数字文字 123
强制插入一行,并且它与来自子选择的 null
值 return 配对。因此 null
试图插入并导致违反约束。
如果您强制 INSERT...SELECT
到 return 具有空值的行,您可能会由于违反约束而失败:
INSERT INTO Test_Child (fid)
-- Return a literal NULL row...
SELECT NULL as id FROM Test_Parent
-- Column fid cannot be null
我遇到了 MySQL 5.6 InnoDb 在 运行 和 INSERT INTO xxx (col) SELECT ...
时忽略 NOT NULL 外键的问题。当 运行 插入其他格式的语句时,约束会被正确执行。外键检查已启用,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
我不明白为什么 MySQL 会对 3 个插入语句中的 2 个强制执行外键。有什么想法吗?
来自您客户的误导性 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
所以看看误导性的受影响的行消息,这里真正发生的是 INSERT...SELECT
语句的 SELECT
部分不匹配任何行,因此 MySQL 不会产生尝试插入任何行。因此没有违反外键约束。
INSERT INTO...SELECT
的格式与您后面的示例略有不同:
INSERT INTO Test_Child(id, fid)
VALUES (123, (SELECT id FROM Test_Parent WHERE dummy = 'missing value'));
...因为在这种情况下,数字文字 123
强制插入一行,并且它与来自子选择的 null
值 return 配对。因此 null
试图插入并导致违反约束。
如果您强制 INSERT...SELECT
到 return 具有空值的行,您可能会由于违反约束而失败:
INSERT INTO Test_Child (fid)
-- Return a literal NULL row...
SELECT NULL as id FROM Test_Parent
-- Column fid cannot be null