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


11

我有下表

CREATE TABLE users (id int PRIMARY KEY);

-- already exists with data
CREATE TABLE message ();

我该如何更改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

此语句是否也不会创建该列?


3
您需要先创建该列,然后再引用它。我还将尝试在此处阅读ALTER TABLE的文档,并密切注意示例。
卡桑德里(Kassandry)

哈桑,我清理了这个问题以使用DDL,并删除了无法正常工作的内容。看看是否能回答问题:dba.stackexchange.com/a/202564/2639。随意拒绝任何这些编辑,我只是​​想清理一下以备后代之用。
埃文·卡罗尔

Answers:


18

相对容易-您只需添加另一步骤。

FOREIGN KEY必须存在才能使其成为FK。我做了以下操作(从此处文档开始):

CREATE TABLE x(t INT PRIMARY KEY);

CREATE TABLE y(s INT);

ALTER TABLE y ADD COLUMN z INT;    

ALTER TABLE y
  ADD CONSTRAINT y_x_fkey FOREIGN KEY (z)
      REFERENCES x (t)
      ON UPDATE CASCADE ON DELETE CASCADE;

需要注意的几点:

始终为外键赋予有意义的名称。被告知密钥“ SYS_C00308108”被违反不是很有帮助。在这种情况下,请参阅此处的小提琴以了解Oracle在这种情况下的行为。键名因小提琴而异,但它是一些以SYS_开头的任意字符串。

考虑您的陈述:

ALTER TABLE message ADD FOREIGN KEY (sender) REFERENCES users;

如果RDBMS可以使用与引用字段匹配的数据类型自动创建所需的字段,那将是一件“精打细算”的事情。我要说的是,更改DDL是(至少应该是)很少使用的操作,而不是您希望定期执行的操作。它还可能会增加已经相当丰富的文档。

至少PostgreSQL试图做一些合理的事情-它连接了表名,FOREIGN KEY字段名_fkey,甚至增加了DETAIL: Key (sender_id)=(56) is not present in table "user_".对人类有意义的东西-参见此处的小提琴。


2
我从来没有命名我的外键。它们会自动命名,通常非常有用。例如,该上下文中的默认名称为"y_z_fkey"。我认为这比一个更好的名字y_x_fkey,因为你违反不会告诉你该列所插入这是造成错误。我不太在乎它的指向。通常,永远不要命名fkey,并让PostgreSQL的默认键处理它。
埃文·卡罗尔

另外,您可能也不想覆盖ON UPDATE CASCADE ON DELETE CASCADE;示例中的默认值,尤其是没有理由的情况下。它使示例更加复杂,您无需理会它的含义。我通常不希望删除级联。
埃文·卡罗尔

1
总是根据公司/项目决定的惯例来命名 FK。它没有多大关系,如果它是y_x_fkey或者y_z_fkey还是x__y_FK,只要它是一致的。
ypercubeᵀᴹ

如果您签订合同,我将非常同意这一点-选择一个约定并遵守该约定,和/或确保您遵守以前与该系统一起使用的约定。
Vérace

@EvanCarroll- 如果 PostgreSQL的约定是项目的约定,或者以前在非PostgreSQL的系统上确定的约定-一个系统很可能已经在Oracle或其他可能没有PostgreSQL约定的系统上启动。您可能会争辩说,如果发生错误,x_y_z_fk可能会提供尽可能多的信息!选择一个东西并坚持下去是我的座右铭,但是不要让一个RDBMS(无论多么出色)为您决定约定!
Vérace

8

我不确定为什么每个人都告诉您必须分两个步骤进行操作。实际上,您没有。您尝试添加一个a FOREIGN KEY,根据设计,该假设假定该列在那里,并且如果该列不存在则抛出该错误。如果添加的COLUMN,你可以明确地让它FOREIGN KEY与创作REFERENCES

ALTER TABLE message
  ADD COLUMN sender INT
  REFERENCES users;  -- or REFERENCES table(unique_column)

将正常工作。您可以ALTER TABLE这里看到语法,

ALTER TABLE [ IF EXISTS ] [ ONLY ] name [ * ]
action [, ... ]

“动作”作为,

ADD [ COLUMN ] [ IF NOT EXISTS ] column_name data_type [ COLLATE collation ] [ column_constraint [ ... ] ]

这些示例甚至在文档中,

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;

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


0

案例1: 如果您需要在创建新表时创建外键

CREATE TABLE table1(
id SERIAL PRIMARY KEY,
column1 varchar(n) NOT NULL,
table2_id SMALLINT REFERENCES table2(id)
); 

上面的命令将创建一个名为“ table1”的表,并命名为“ id”(主键),“ column1”,“ table2_id”(表1的外键引用表2的id列)三列。

DATATYPE'serial'将使使用此数据类型的列作为自动生成的列,当在表中插入值时,您根本不需要提及此列,或者您可以在值位置使用'default'而不带引号。

主键列始终以值'tablename_pkey'添加到表的索引中。

如果在表创建时添加了外键,则使用格式'(present_table_name)_(foreign_key_id_name)_fkey'添加一个CONSTRAINT。

添加外键时,我们必须在列名称旁边输入关键字“ REFERENCES”,因为我们要告诉postgres该列引用了一个表,然后在引用旁​​边我们必须将该表提供给引用,并在方括号中给出引用表的列名,通常将外键作为主键列。

情况2: 如果您想要外键指向现有列上的现有表

ALTER TABLE table1
ADD CONSTRAINT table1_table2_id_id_fkey
FOREIGN KEY (table2_id) REFERENCES table2(id);

注意:FOREIGN KEY和REFERENCES tabel2之后的方括号'()'是强制性的,否则postgres将抛出错误。


0

我知道这个问题。列名不同。也许在一个列中,您的列名后面会有一个空格,因此请仔细确保您的列名命名完全相同。


1
OP问:此语句是否也不会创建该列?因此,很明显,他希望这种情况会发生。
Laurenz Albe
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.