Como adicionar uma coluna com uma restrição de chave estrangeira a uma tabela que já existe?

11

Eu tenho as seguintes tabelas,

CREATE TABLE users (id int PRIMARY KEY);

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

Como altero a messagestabela de modo que,

  1. uma nova coluna chamada senderé adicionada a ela
  2. onde senderé uma chave estrangeira referenciando a userstabela

Isso não funcionou

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

Esta declaração não cria a coluna também?

Hassan Baig
fonte
3
Você precisa criar a coluna antes de fazer referência a ela. Eu também tentaria ler a documentação do ALTER TABLE aqui e prestaria muita atenção aos exemplos.
Kassandry
Hassan, limpei esta questão para usar DDL e removi as coisas que não estavam funcionando. Veja se isso responde à pergunta: dba.stackexchange.com/a/202564/2639 . Sinta-se livre para rejeitar qualquer uma dessas edições, eu só queria limpar isso para a posteridade.
Evan Carroll
@Kassandry dba.stackexchange.com/a/202564/2639
Evan Carroll

Respostas:

18

O que é relativamente fácil - você apenas precisa adicionar outra etapa.

A FOREIGN KEYcoluna precisa existir para torná-la uma FK. Eu fiz o seguinte (a partir daqui e a documentação ):

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;

Alguns pontos a serem observados:

SEMPRE dê nomes significativos às suas chaves estrangeiras. Ser informado de que a chave "SYS_C00308108" está sendo violada não é muito útil. Veja aqui o violino para o comportamento do Oracle nessas circunstâncias. O nome da chave variará de violino para violino, mas é uma string arbitrária que começa com SYS _...)

Considerando sua declaração:

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

Seria "bom de ter" se o RDBMS pudesse criar automaticamente o campo desejado com o tipo de dados correspondente ao campo referenciado. Tudo o que eu diria é que alterar o DDL é (ou pelo menos deveria ser) uma operação raramente usada e não algo que você gostaria de fazer regularmente. Também corre o risco de adicionar a uma documentação já bastante substancial.

Pelo menos o PostgreSQL tenta fazer algo razoável - concatena o nome da tabela, o nome do FOREIGN KEYcampo _fkeye até acrescenta DETAIL: Key (sender_id)=(56) is not present in table "user_".algo que possa fazer sentido para um ser humano - veja aqui .

Vérace
fonte
2
Eu nunca nomeio minhas chaves estrangeiras. Eles são nomeados automaticamente e geralmente são bastante úteis. Por exemplo, o nome padrão nesse contexto é "y_z_fkey". Eu diria que é um nome melhor do que y_x_fkeyporque sua violação não lhe diz a coluna que você está inserindo em que está causando o erro. Eu me preocupo menos com o que está apontando. Como regra geral, você NUNCA deve nomear suas chaves e deixar o padrão do PostgreSQL lidar com isso.
Evan Carroll
Além disso, você também não pode substituir os padrões de ON UPDATE CASCADE ON DELETE CASCADE;um exemplo, especialmente sem motivo. Isso torna o exemplo mais complexo e você não se importa em explicar o que é. Eu, por exemplo, normalmente não quero excluir em cascata.
Evan Carroll
11
Eu sempre nomeio FKs, de acordo com a convenção que a empresa / projeto decidiu. Não importa muito se é y_x_fkeyou y_z_fkeyou x__y_FK, desde que seja consistente.
precisa saber é o seguinte
Eu concordaria muito com isso se você estiver contratando - escolha uma convenção e cumpra-a e / ou garanta que esteja em conformidade com as convenções que foram / foram usadas anteriormente com o sistema.
Vérace 28/03
@EvanCarroll - se a convenção do PostgreSQL for a do projeto ou a que foi decidida anteriormente em sistemas que podem não ser o PostgreSQL - um sistema pode ter começado, digamos, Oracle ou outro sistema que pode não ter a (s) convenção (s) do PostgreSQL. Você pode argumentar que x_y_z_fk pode fornecer o máximo de informações possível no caso de um erro! Escolha algo e cumpra -o é o meu lema, mas não deixe que um RDBMS (por melhor que seja) decida convenções para você!
Vérace 28/03
8

Não sei por que todo mundo está lhe dizendo que você precisa fazer isso em duas etapas. De fato, você não . Você tentou adicionar um FOREIGN KEYque assume, por design, que a coluna está lá e gera esse erro se a coluna não estiver lá. Se você adicionar o COLUMN, poderá explicitamente torná-lo FOREIGN KEYna criação com REFERENCES,

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

Vai funcionar bem. Você pode ver a sintaxe ALTER TABLEdaqui,

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

Com "ação" como,

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

Esses exemplos estão nos documentos,

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;

Mas tudo isso não é necessário, porque podemos confiar no nome automático e na resolução da chave primária (se apenas o nome da tabela for especificado, você fará referência à chave primária).

Evan Carroll
fonte
0

CASE1: Se você precisar criar chave estrangeira ao criar uma nova tabela

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

Os comandos acima criarão uma tabela com o nome 'table1' e três colunas denominadas 'id' (chave primária), 'column1', 'table2_id' (chave estrangeira da tabela1 que faz referência à coluna de identificação da tabela2).

DATATYPE 'serial' criará a coluna que usa esse tipo de dados como uma coluna gerada automaticamente. Ao inserir valores na tabela, você não precisa mencionar essa coluna ou pode dar 'padrão' sem aspas no local do valor.

Uma coluna de chave primária é sempre adicionada ao índice da tabela com o valor 'tablename_pkey'.

Se a chave estrangeira for adicionada no momento da criação da tabela, A CONSTRAINT será adicionado com o padrão '(present_table_name) _ (Foreign_key_id_name) _fkey'.

Ao adicionar uma chave estrangeira, precisamos inserir a palavra-chave 'REFERENCES' ao lado do nome da coluna, porque queremos dizer ao postgres que esta coluna faz referência a uma tabela e, ao lado das referências, temos que dar a tabela para referência e entre colchetes, dar o nome da coluna da tabela referenciada, geralmente chaves estrangeiras são fornecidas como colunas de chave primária.

CASO 2: Se você deseja chave estrangeira para uma tabela existente na coluna existente

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

NOTA: colchetes '()' após FOREIGN KEY e REFERENCES tabel2 são obrigatórios ou o postgres gerará erro.

Ashok Allu
fonte
0

Eu conheço o problema. Os nomes das colunas são diferentes. Talvez em uma coluna haja um espaço adicional após o nome da coluna, portanto, verifique cuidadosamente se os nomes das colunas foram exatamente iguais.

XIN WANG
fonte
11
OP perguntou: Esta declaração não cria a coluna também? Portanto, é evidente que ele esperava que isso acontecesse.
Laurenz Albe 06/04