Как добавить столбец с ограничением внешнего ключа в таблицу, которая уже существует?

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 здесь и уделить очень пристальное внимание примерам.
Кассандри
Хасан, я убрал этот вопрос, чтобы использовать DDL, и я удалил вещи, которые не работали. Посмотрите, отвечает ли это на вопрос: dba.stackexchange.com/a/202564/2639 . Не стесняйтесь отклонить любое из этих правок, я просто хотел очистить это для потомков.
Эван Кэрролл
@Kassandry dba.stackexchange.com/a/202564/2639
Эван Кэрролл,

Ответы:

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;

Было бы неплохо, если бы СУБД могла автоматически создавать желаемое поле с типом данных, соответствующим указанному полю. Все, что я хотел бы сказать, это то, что изменение DDL является (или, по крайней мере, должно быть) редко используемой операцией, а не тем, чем вы хотели бы заниматься регулярно. Это также рискует добавить к уже достаточно содержательной документации.

По крайней мере, PostgreSQL пытается сделать что-то разумное - он объединяет имя таблицы, имя FOREIGN KEYполя _fkeyи даже добавляет, DETAIL: Key (sender_id)=(56) is not present in table "user_".чтобы дать что-то, что может иметь смысл для человека - см. Скрипку здесь .

Verace
источник
2
Я никогда не называю свои внешние ключи. Они получают автономное имя, и они обычно довольно полезны. Например, имя по умолчанию в этом контексте "y_z_fkey". Я бы утверждать , что это лучше , чем имя , y_x_fkeyпотому что ваше нарушение не говорят вам , колонка вы вставка в это вызывает ошибку. Меня не волнует, куда он указывает. Как правило, вы НИКОГДА не должны называть свои fkeys и позволить стандартному PostgreSQL обрабатывать его.
Эван Кэрролл,
Кроме того, вы можете не захотеть переопределять значения по умолчанию ON UPDATE CASCADE ON DELETE CASCADE;в примере, особенно без причины. Это делает пример более сложным, и вы не будете объяснять, что это такое. Я, например, обычно не хочу, чтобы удаления каскадом.
Эван Кэрролл
1
Я всегда называю FKs, в соответствии с соглашением, которое определила компания / проект. Не имеет большого значения, если это y_x_fkeyили y_z_fkeyили x__y_FK, если это соответствует.
ypercubeᵀᴹ
Я бы очень согласился с этим, если вы заключаете договор - выберите соглашение и придерживайтесь его и / или убедитесь, что вы соответствуете соглашению (ям), которые использовались / использовались с системой ранее.
Vérace
@EvanCarroll - если соглашение PostgreSQL относится к проекту или ранее было принято решение о системах, которые могут не являться PostgreSQL, - система вполне могла начать, скажем, Oracle или другую систему, которая может не иметь соглашения (соглашений) PostgreSQL. Вы можете утверждать, что x_y_z_fk может дать максимально возможную информацию в случае ошибки! Выберите что-то и придерживайтесь этого - мой девиз, но не позволяйте одной СУРБД (независимо от того, насколько она хороша) решать за вас соглашения!
Vérace
8

Я не уверен, почему все говорят вам, что вы должны сделать это в два этапа. На самом деле, вы не делаете . Вы попытались добавить элемент, 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

CASE1: если вам нужно создать внешний ключ при создании новой таблицы

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

Приведенные выше команды создадут таблицу с именем «table1» и тремя столбцами с именами «id» (первичный ключ), «column1», «table2_id» (внешний ключ table1, который ссылается на столбец id таблицы table2).

DATATYPE 'serial' сделает столбец, который использует этот тип данных, как автоматически сгенерированный столбец, при вставке значений в таблицу вам вообще не нужно упоминать этот столбец, или вы можете указать 'default' без кавычек в месте значения.

Столбец первичного ключа всегда добавляется в индекс таблицы со значением tablename_pkey.

Если внешний ключ добавляется во время создания таблицы, добавляется CONSTRAINT с шаблоном '(present_table_name) _ (foreign_key_id_name) _fkey'.

При добавлении внешнего ключа мы должны ввести ключевое слово «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
ОП спросил: это утверждение не создает колонку? Таким образом, очевидно, что он ожидал, что это произойдет.
Лоренц Альб