Come aggiungere una colonna con un vincolo di chiave esterna a una tabella già esistente?


11

Ho le seguenti tabelle,

CREATE TABLE users (id int PRIMARY KEY);

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

Come posso modificare la messagestabella in modo tale che,

  1. senderviene aggiunta una nuova colonna chiamata
  2. dove senderè una chiave esterna che fa riferimento alla userstabella

Questo non ha funzionato

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

Questa affermazione non crea anche la colonna?


3
È necessario creare la colonna prima di fare riferimento a essa. Vorrei anche provare a leggere la documentazione di ALTER TABLE qui e prestare molta attenzione agli esempi.
Kassandry,

Hassan, ho ripulito questa domanda per usare DDL e ho rimosso le cose che non funzionavano. Verifica se questo risponde alla domanda: dba.stackexchange.com/a/202564/2639 . Sentiti libero di rifiutare una qualsiasi di queste modifiche, volevo solo ripulirlo per i posteri.
Evan Carroll,

Risposte:


18

Cosa è relativamente semplice: devi solo aggiungere un altro passaggio.

La FOREIGN KEYcolonna deve esistere per renderla un FK. Ho fatto quanto segue (da qui e la documentazione ):

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;

Un paio di punti da notare:

Dai SEMPRE nomi significativi alle tue chiavi esterne. Non è molto utile sapere che la chiave "SYS_C00308108" è stata violata. Vedi il violino qui per il comportamento di Oracle in queste circostanze il nome della chiave varierà da violino a violino, ma è una stringa arbitraria che inizia con SYS _...)

Considerando la tua affermazione:

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

Sarebbe un "bello da avere" se RDBMS potesse creare automaticamente il campo desiderato con il tipo di dati corrispondente al campo di riferimento. Tutto quello che vorrei dire è che cambiare DDL è (o almeno dovrebbe essere) un'operazione usata raramente e non qualcosa che vorresti fare regolarmente. Rischia inoltre di aggiungere a una documentazione già abbastanza sostanziale.

Almeno PostgreSQL cerca di fare qualcosa di ragionevole - concatena il nome della tabella, il nome del FOREIGN KEYcampo e _fkeye persino aggiunge DETAIL: Key (sender_id)=(56) is not present in table "user_".per dare qualcosa che potrebbe avere senso per un essere umano - vedi violino qui .


2
Non ho mai dato un nome alle mie chiavi esterne. Vengono autonominati e di solito sono piuttosto utili. Ad esempio, il nome predefinito in quel contesto è "y_z_fkey". Direi che è un nome migliore di y_x_fkeyquanto la violazione non vi dice la colonna si sta inserendo in che sta causando l'errore. Mi interessa meno di dove punta. Come regola generale, non dovresti MAI nominare i tuoi tasti e lasciare che sia il default di PostgreSQL a gestirlo.
Evan Carroll,

Inoltre, potresti non voler sovrascrivere le impostazioni predefinite di ON UPDATE CASCADE ON DELETE CASCADE;un esempio, specialmente senza motivo. Rende l'esempio più complesso e non ti preoccupi di spiegare di cosa si tratta. Io per uno di solito non voglio che le eliminazioni cadano in cascata.
Evan Carroll,

1
Io chiamo sempre gli FK, secondo la convenzione che l'azienda / progetto ha deciso. Non importa molto se è y_x_fkeyo y_z_fkeyo x__y_FK, purché sia ​​coerente.
ypercubeᵀᴹ

Sarei molto d'accordo con questo se stai contraendo: scegli una convenzione e aderisci ad essa e / o assicurati di conformarti alle convenzioni che erano / erano state usate con il sistema in precedenza.
Vérace,

@EvanCarroll - se la convenzione di PostgreSQL è quella del progetto o uno precedentemente deciso su sistemi che potrebbero non essere PostgreSQL - un sistema potrebbe essere stato avviato su, diciamo, Oracle o altri sistemi che potrebbero non avere le convenzioni di PostgreSQL. Si potrebbe sostenere che x_y_z_fk potrebbe fornire la massima informazione possibile in caso di errore! Scegliere qualcosa e attenersi ad esso è il mio motto, ma non lasciare che un RDBMS (non importa quanto buono) decida le convenzioni per te!
Vérace,

8

Non sono sicuro del motivo per cui tutti ti stanno dicendo che devi farlo in due passaggi. In effetti no . Hai provato ad aggiungere un FOREIGN KEYche presuppone, in base alla progettazione, che la colonna sia presente e genera tale errore se la colonna non è presente. Se aggiungi il COLUMN, puoi renderlo esplicitamente una FOREIGN KEYcreazione con REFERENCES,

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

Funzionerà bene. Puoi vedere la sintassi di ALTER TABLEqui,

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

Con "azione" come

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

Questi esempi sono persino nei documenti,

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;

Ma tutto ciò non è necessario perché possiamo fare affidamento sul autonaming e sulla risoluzione della chiave primaria (se viene specificato solo il nome della tabella, si fa riferimento alla chiave primaria).


0

CASO 1: se è necessario creare una chiave esterna durante la creazione di una nuova tabella

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

I comandi precedenti creeranno una tabella con il nome 'table1' e tre colonne denominate 'id' (chiave primaria), 'column1', 'table2_id' (chiave esterna della tabella1 che fa riferimento alla colonna id della tabella2).

DATATYPE 'serial' renderà la colonna che utilizza questo tipo di dati come colonna generata automaticamente, quando si inseriscono valori nella tabella non è necessario menzionare questa colonna, oppure si può dare 'default' senza virgolette nella posizione del valore.

Una colonna chiave primaria viene sempre aggiunta all'indice della tabella con il valore 'tablename_pkey'.

Se al momento della creazione della tabella viene aggiunta una chiave esterna, viene aggiunto A VINCITORE con il motivo '(nome_tabella_attuale) _ (nome_chia_id_estero) _fkey'.

Quando si aggiunge una chiave esterna, dobbiamo inserire la parola chiave "RIFERIMENTI" accanto al nome della colonna perché vogliamo dire ai postgres che questa colonna fa riferimento a una tabella e quindi accanto ai riferimenti dobbiamo dare la tabella come riferimento e tra parentesi dare il nome della colonna della tabella referenziata, in genere le chiavi esterne vengono fornite come colonne chiave primaria.

CASO 2: se si desidera una chiave esterna per una tabella esistente su una colonna esistente

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

NOTA: le parentesi '()' dopo FOREIGN KEY e REFERENCES tabel2 sono obbligatorie altrimenti postgres genererà un errore.


0

Conosco il problema. I nomi delle colonne sono diversi. Forse in una colonna, c'è uno spazio aggiunto dopo il nome della colonna, quindi assicurati che i nomi delle colonne siano stati identici.


1
OP chiesto: questa affermazione non crea anche la colonna? Quindi è evidente che si aspettava che ciò accadesse.
Laurenz Albe,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.