【问题标题】:MySQL Insert Select Doesn't Enforce NOT NULL ConstraintMySQL 插入选择不强制执行 NOT NULL 约束
【发布时间】:2017-05-08 16:33:42
【问题描述】:

我遇到了 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 行受影响”,因为 INSERT...SELECT 实际上并没有导致任何行插入,因为 WHERE 子句不匹配。如果我从两个表中SELECT *,我在Test_Child 中看不到任何行...sqlfiddle.com/#!9/7413e0/1
  • 也就是说,除非您的真实数据和用例与您为我们设置的测试集有显着差异。
  • 没错 - 它报告插入了 1 行,但实际上没有任何内容插入到表中。我希望在这种情况下强制执行 NOT NULL 约束。相反,就好像异常被吞没了一样。
  • 但是在你的真实测试数据中,WHERE dummy = 'missing value' 真的返回一行吗?如果不是,它将始终导致插入零行,因此不会违反 FK。但是您的 MySQL 版本可能在受影响的行输出中存在错误。我在 5.7 和 5.6.35 上尝试过,但我的两个都报告插入了零行而不是 1。
  • 因为如果没有来自SELECT 的行,它甚至不会尝试插入。它与您的第三个示例不同,其中文字 123 强制插入行以及从子选择返回的 NULL。如果您尝试了类似 INSERT INTO Test_Child (fid) SELECT NULL FROM Test_Parent 的方法,其中 select 确实返回了具有 NULL 值的行,那么您应该会遇到 FK 违规。

标签: mysql foreign-keys constraints notnull insert-select


【解决方案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

因此,查看误导性的受影响行消息,这里真正发生的是 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 值配对。因此,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

【讨论】:

    猜你喜欢
    • 2014-12-25
    • 2020-01-21
    • 1970-01-01
    • 2011-09-07
    • 2013-11-29
    • 2018-11-10
    • 1970-01-01
    • 2018-12-16
    • 1970-01-01
    相关资源
    最近更新 更多