如何将具有外键约束的列添加到已存在的表中?

Has*_*aig 17 postgresql foreign-key alter-table

我有以下表格,

CREATE TABLE users (id int PRIMARY KEY);

-- already exists with data
CREATE TABLE message ();
Run Code Online (Sandbox Code Playgroud)

我如何改变messages表格,

  1. 一个名为的新列sender被添加到其中
  2. sender引用users表的外键在哪里

这没有用

# ALTER TABLE message ADD FOREIGN KEY (sender) REFERENCES users;
ERROR:  column "sender" referenced in foreign key constraint does not exist
Run Code Online (Sandbox Code Playgroud)

这个语句也不会创建列吗?

Vér*_*ace 23

您只需要添加另一个步骤 - 事实上 PostgreSQL 已经告诉您:column "sender" referenced in foreign key constraint does not exist.

FOREIGN KEY(又名parent)列必须已经存在,以使它的FK

我做了以下(从这里文档)。请注意,父列必须有一个UNIQUE约束(或者是PRIMARY KEY),但它不一定是NOT NULL。这是因为NULLs 彼此不相等,也不等于其他任何东西 - 每个都NULL被视为UNIQUE自己的权利!

CREATE TABLE x(a INT UNIQUE NOT NULL);

CREATE TABLE y(b INT);

ALTER TABLE y ADD COLUMN c INT NOT NULL
CONSTRAINT y_x_fk_c REFERENCES x (a)   -- if x (a) doens't exist, this will fail!
ON UPDATE CASCADE ON DELETE CASCADE;  -- or other Referential Integrity Action
Run Code Online (Sandbox Code Playgroud)

需要注意的几点(请参阅此处的小提琴) - 尝试将不在 x (a) 中的值插入 y (c) 中失败,并且错误消息中给出了约束名称。

小提琴NOT NULL对 x (a) 和 y (c)有约束。除非我有真正令人信服的理由,否则我总是将我的列声明为NOT NULL- 它有助于优化器并减少混淆/错误的可能性。你可以用小提琴来试验自己,看看当你省略NOT NULL一个(和两个)字段时会发生什么- 行为并不总是直观明显!

总是给你的外键起有意义的名字。被告知密钥“SYS_C00308108”被违反并不是很有帮助。在这些情况下,请参阅此处的小提琴了解 Oracle 在这些情况下的行为,密钥名称因小提琴而异,但是以 SYS_... 开头的任意字符串(在长 dbfiddle 生成的表名之后)。

埃文·卡罗尔在他的答案在这里认为,自动生成的名字是好的-我已经证明这是为什么为Oracle是一个好主意(至少高达18℃),但我也觉得这不是对PostgreSQL无论是一个好主意-如果没有别的,便携性的潜在问题。

我想信贷埃文·卡罗尔的指出了新领域的增加,并FOREIGN KEY创建和CONSTRAINT(与指定的名称)可以在一个步骤中添加,而不是两个步骤我原来说的) -所以,请给他的信用为如果你想给我点赞——不过我会更详细地介绍。

考虑到您的问题中的陈述:

ALTER TABLE message ADD FOREIGN KEY (sender) REFERENCES users;
Run Code Online (Sandbox Code Playgroud)

如果 RDBMS 可以使用与引用字段匹配的数据类型自动创建您想要的字段,那将是一个“不错的选择”。

我想说的是,更改 DDL 是(或至少应该是)很少使用的操作,而不是您想要定期执行的操作。它还冒着增加已经相当重要的文档的风险。

至少 PostgreSQL 尝试做一些合理的事情——它将表名、FOREIGN KEY字段名和fkey. 此外,当您自己命名约束时,错误消息将添加DETAIL: Key (c)=(7) is not present in table "x".以提供对人类可能有意义的东西(与 Oracle 不同 - 请参阅 PostgreSQL fiddle的结尾)。

  • 我从不命名我的外键。它们会自动命名,而且通常非常有用。例如,该上下文中的默认名称是“y_z_fkey”。我认为这是一个比 `y_x_fkey` 更好的名称,因为你的违规没有告诉你你正在插入 *into* 的列导致错误。我不太关心它指向哪里。作为一般规则,你应该 **NEVER** 命名你的 fkeys 并让 PostgreSQL 的默认处理它。 (2认同)

Eva*_*oll 12

我不知道为什么每个人都告诉你必须分两步完成。事实上,你没有。您尝试添加 a FOREIGN KEY,它根据设计假定该列存在,如果该列不存在,则会引发该错误。如果添加COLUMN,则可以FOREIGN KEY使用REFERENCES

ALTER TABLE message
  ADD COLUMN sender INT
  REFERENCES users;  -- or REFERENCES table(unique_column)
Run Code Online (Sandbox Code Playgroud)

会工作得很好。你可以看到ALTER TABLE这里的语法,

ALTER TABLE [ IF EXISTS ] [ ONLY ] name [ * ]
action [, ... ]
Run Code Online (Sandbox Code Playgroud)

“行动”为,

ADD [ COLUMN ] [ IF NOT EXISTS ] column_name data_type [ COLLATE collation ] [ column_constraint [ ... ] ]
Run Code Online (Sandbox Code Playgroud)

这些例子甚至在文档中,

ALTER TABLE distributors
  ADD CONSTRAINT distfk
  FOREIGN KEY (address)
  REFERENCES addresses (address);

ALTER TABLE distributors
  ADD CONSTRAINT distfk
  FOREIGN KEY (address)
  REFERENCES addresses (address)
  NOT VALID;
Run Code Online (Sandbox Code Playgroud)

但所有这些都不是必需的,因为我们可以依靠自动命名和主键解析(如果只指定了表名,那么您正在引用主键)。