ChatGPT解决这个技术问题 Extra ChatGPT

具有外键的表列可以为 NULL 吗?

我有一个表,其中有几个 ID 列到其他表。

只有当我将数据放在那里时,我才想要一个外键来强制完整性。如果我稍后进行更新以填充该列,那么它还应该检查约束。

(这可能取决于数据库服务器,我使用的是 MySQL 和 InnoDB 表类型)

我相信这是一个合理的期望,但如果我错了,请纠正我。

我不了解 MySQL,但 MS SQL Server 允许外键可以为您想要的语义为空。我希望这是标准行为。
外键,在 mySQL 中默认不能为空,原因很简单,如果你引用了一些东西,你让它为空,你会失去数据的完整性。当您创建表集时,允许 null 为 NOT,然后应用外键约束。您不能在更新时设置 null,它应该会向您发送错误,但您可以(您必须)简单地不更新此列并仅更新您需要更改的字段。

S
Softlion

是的,您只能在值不为 NULL 时强制执行约束。这可以通过以下示例轻松测试:

CREATE DATABASE t;
USE t;

CREATE TABLE parent (id INT NOT NULL,
                     PRIMARY KEY (id)
) ENGINE=INNODB;

CREATE TABLE child (id INT NULL, 
                    parent_id INT NULL,
                    FOREIGN KEY (parent_id) REFERENCES parent(id)
) ENGINE=INNODB;


INSERT INTO child (id, parent_id) VALUES (1, NULL);
-- Query OK, 1 row affected (0.01 sec)


INSERT INTO child (id, parent_id) VALUES (2, 1);

-- ERROR 1452 (23000): Cannot add or update a child row: a foreign key 
-- constraint fails (`t/child`, CONSTRAINT `child_ibfk_1` FOREIGN KEY
-- (`parent_id`) REFERENCES `parent` (`id`))

第一次插入将通过,因为我们在 parent_id 中插入了一个 NULL。由于外键约束,第二次插入失败,因为我们试图插入 parent 表中不存在的值。


也可以使用 id INT NOT NULL 声明父表。
@CJDennis 如果你这样做,只有一行可以有一个空 ID,它可以用作其他行的后备值。 (尽管如果您只使用更多列,它可能对 DB 更有效。)如果您想稍后知道某个值是最初设置为“默认”(通过使用 null)还是设置为恰好与“默认”相同的值。通过拥有一个具有空 id 的行,您可以清楚地表明该行不能用作普通行,并且可以将该行用作为其他行提供某种动态默认值的一种方式。
我认为 parent_id INT NULL 部分(详细)等于 parent_id int default null
java 用户的旁注,如果您使用 ibatis 或其他 ORM 并在您的类成员中使用原始 int 而不是 Integer,则默认值永远不会为 null,而是 0,您将失败约束。
它可能适用于插入,但在删除源数据时会发生错误:Error Code: 1451. Cannot delete or update a parent row: a foreign key constraint fails
B
Backslider

我发现在插入时,必须将空列值明确声明为 NULL,否则我会收到约束违规错误(而不是空字符串)。


您不能在列上设置默认值 NULL 以允许这样做吗?
是的,在大多数语言中,NULL 与空字符串不同。开始时可能很微妙,但要记住很重要。
嘿,Backslider,你说“(而不是空字符串)”,但我不认为你的意思是你会插入一个空字符串的值,而是你没有为 Value at 指定一个值全部?即,您甚至没有在 INSERT INTO {table} {list_of_columns} 中提及该列?因为这对我来说是真的;省略列会导致错误,但包括并显式设置为 NULL 可修复错误。如果我是对的,我认为@Gary 的评论不适用(因为你的意思不是空字符串),但@Kevin Coulombe 的评论可能会有所帮助......
是的,@KevinCoulombe 的建议有效,我描述了如何使用 Entity Framework Core 的迁移脚本来实现这一点,here
需要指出的是,在更新包含 NULL 外键的记录时明确的理由仅适用于字符串类型(varchar 等),因为否则可以将空字符串作为默认值传递。 MySQL 就是这种情况,并导致更新时出现完整性错误。
d
davidtbernal

是的,这将按您的预期工作。不幸的是,我似乎很难在 MySQL manual 中找到明确的声明。

外键意味着该值必须存在于另一个表中。 NULL 是指没有值,因此当您将列设置为 NULL 时,尝试对其施加约束是没有意义的。


按照设计外键必须引用一些非NULL的键(Primary),但是在开发阶段,当我们需要首先将多个数据插入到子表中时,我们不知道它会引用谁(父表) .这就是我们允许 NULL 值的原因。在生产中具有 NULL 将是一个设计流程,可以粗略地说。
@vimalkrishna 不一定 - 主键不能包含 null,但如果子元素不一定有父元素,则外键可以为 null。例如,如果父元素是仓库,而子元素是产品,则 NULL 值可能表示产品当前未存储在仓库中。这比创建一个虚拟的“无仓库”仓库来满足不允许空值的外键约束更有意义。
T
Toby Crain

是的,该值可以为 NULL,但您必须明确。我以前也经历过同样的情况,很容易忘记为什么会发生这种情况,所以需要一点时间来记住需要做什么。

如果提交的数据被强制转换或解释为空字符串,它将失败。但是,通过在 INSERTING 或 UPDATING 时将值显式设置为 NULL,您就可以开始了。

但这就是编程的乐趣,不是吗?创建我们自己的问题,然后解决它们!干杯!


R
Rich

上述工作,但事实并非如此。注意 ON DELETE CASCADE

CREATE DATABASE t;
USE t;

CREATE TABLE parent (id INT NOT NULL,
                 PRIMARY KEY (id)
) ENGINE=INNODB;

CREATE TABLE child (id INT NULL, 
                parent_id INT NULL,
                FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE

) ENGINE=INNODB;


INSERT INTO child (id, parent_id) VALUES (1, NULL);
-- Query OK, 1 row affected (0.01 sec)

你所说的“以上”是什么意思?请注意,如果您指的是另一个答案,则顺序可能会改变。
W
Wildhammer

解决此问题的另一种方法是在另一个表中插入一个 DEFAULT 元素。例如,在另一个表上对 uuid=00000000-0000-0000-0000-000000000000 的任何引用都表示没有操作。您还需要将该 id 的所有值设置为“中性”,例如 0、空字符串、null,以免影响您的代码逻辑。


这不是一回事。默认值或“中性”值与 NULL 不同,即没有值。在不讨论默认值优于 NULL 的情况下,您的措辞有点混杂。 “另一种解决方法是在另一个表中插入一个空元素”应该说更像“另一种解决方法是在另一个表中插入一个 DEFAULT 元素”
S
Shams Reza

我也坚持这个问题。但我只是通过将外键定义为 unsigned integer 来解决。找到下面的例子 -

CREATE TABLE parent (
   id int(10) UNSIGNED NOT NULL,
    PRIMARY KEY (id)
) ENGINE=INNODB;

CREATE TABLE child (
    id int(10) UNSIGNED NOT NULL,
    parent_id int(10) UNSIGNED DEFAULT NULL,
    FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE
) ENGINE=INNODB;