¿Cómo agregar una columna con una restricción de clave externa a una tabla que ya existe?

11

Tengo las siguientes tablas,

CREATE TABLE users (id int PRIMARY KEY);

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

¿Cómo modifico la messagestabla de manera que,

  1. senderse le agrega una nueva columna llamada
  2. donde senderhay una clave externa que hace referencia a la userstabla

Esto no funciono

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

¿Esta declaración no crea la columna también?

Hassan Baig
fuente
3
Debe crear la columna antes de hacer referencia a ella. También intentaría leer la documentación de ALTER TABLE aquí, y prestaré mucha atención a los ejemplos.
Kassandry
Hassan, limpié esta pregunta para usar DDL y eliminé las cosas que no funcionaban. Vea si esto responde la pregunta: dba.stackexchange.com/a/202564/2639 . Siéntase libre de rechazar cualquiera de estas ediciones, solo quería limpiar esto para la posteridad.
Evan Carroll
@Kassandry dba.stackexchange.com/a/202564/2639
Evan Carroll

Respuestas:

18

Lo que es relativamente fácil: solo tiene que agregar otro paso.

La FOREIGN KEYcolumna tiene que existir para que sea una FK. Hice lo siguiente (desde aquí y la documentación ):

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 par de puntos a tener en cuenta:

SIEMPRE dé a sus claves foráneas nombres significativos. Que nos digan que se está violando la clave "SYS_C00308108" no es muy útil. Vea el violín aquí para ver el comportamiento de Oracle en estas circunstancias, el nombre de la clave variará de un violín a otro, pero es una cadena arbitraria que comienza con SYS _...)

Considerando su declaración:

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

Sería "agradable tenerlo" si el RDBMS pudiera crear automáticamente el campo que desea con el tipo de datos que coincida con el campo referenciado. Todo lo que diría es que cambiar DDL es (o al menos debería ser) una operación raramente utilizada y no algo que desearía hacer regularmente. También corre el riesgo de agregarse a una documentación ya bastante sustancial.

Al menos PostgreSQL intenta hacer algo razonable: concatena el nombre de la tabla, el nombre del FOREIGN KEYcampo _fkeye incluso se agrega DETAIL: Key (sender_id)=(56) is not present in table "user_".para dar algo que podría tener sentido para un ser humano; vea el violín aquí .

Vérace
fuente
2
Nunca nombro mis claves foráneas. Se autonlama y generalmente son bastante útiles. Por ejemplo, el nombre predeterminado en ese contexto es "y_z_fkey". Yo diría que es un nombre mejor que y_x_fkeydebido a que su violación no le dice la columna vas a insertar en el que está causando el error. Me importa menos dónde está apuntando. Como regla general, NUNCA debe nombrar sus fkeys y dejar que el valor predeterminado de PostgreSQL lo maneje.
Evan Carroll
Además, es posible que tampoco desee anular los valores predeterminados de ON UPDATE CASCADE ON DELETE CASCADE;en un ejemplo, especialmente sin razón. Hace que el ejemplo sea más complejo y no te molestes en explicar de qué se trata. Por mi parte, normalmente no quiero eliminaciones en cascada.
Evan Carroll
1
Yo siempre el nombre FKs, de acuerdo con la convención de que la empresa / proyecto ha decidido. No importa mucho si es y_x_fkeyo y_z_fkeyo x__y_FK, siempre y cuando sea consistente.
ypercubeᵀᴹ
Estoy muy de acuerdo con esto si está contratando: elija una convención y cúmplala y / o asegúrese de cumplir con las convenciones que se usaron con el sistema anteriormente.
Vérace
@EvanCarroll: si la convención de PostgreSQL es la del proyecto o una que se haya decidido previamente en sistemas que podrían no ser PostgreSQL, es posible que un sistema haya comenzado en, por ejemplo, Oracle u otro sistema que no tenga las convenciones de PostgreSQL. ¡Podría argumentar que x_y_z_fk podría proporcionar la máxima información posible en caso de error! ¡Elegir algo y apegarse a él es mi lema, pero no dejes que un RDBMS (no importa qué tan bueno) decida las convenciones para ti!
Vérace
8

No estoy seguro de por qué todos te dicen que debes hacer esto en dos pasos. De hecho, no lo haces . Intentó agregar uno FOREIGN KEYque asume, por diseño, que la columna está allí y arroja ese error si la columna no está allí. Si agrega el COLUMN, puede convertirlo explícitamente en una FOREIGN KEYcreación con REFERENCES,

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

Funcionará bien Puedes ver la sintaxis de ALTER TABLEaquí,

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

Con "acción" como,

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

Estos ejemplos están incluso en los 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;

Pero todo eso no es necesario porque podemos confiar en el autonaming y la resolución de la clave principal (si solo se especifica el nombre de la tabla, entonces está haciendo referencia a la clave principal).

Evan Carroll
fuente
0

CASO1: si necesita crear una clave externa mientras crea una nueva tabla

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

Los comandos anteriores crearán una tabla con el nombre 'table1' y tres columnas llamadas 'id' (Clave primaria), 'column1', 'table2_id' (clave foránea de table1 que hace referencia a la columna id de table2).

DATATYPE 'serial' hará que la columna que usa este tipo de datos como una columna generada automáticamente, cuando inserte valores en la tabla no necesite mencionar esta columna en absoluto, o puede dar 'default' sin comillas en el lugar del valor.

Siempre se agrega una columna de clave principal al índice de la tabla con el valor 'tablename_pkey'.

Si se agrega una clave foránea en el momento de la creación de la tabla, se agrega una CONSTRAINT con el patrón '(present_table_name) _ (foreign_key_id_name) _fkey'.

Al agregar una clave foránea, debemos ingresar la palabra clave 'REFERENCES' al lado del nombre de la columna porque queremos decirle a los postgres que esta columna hace referencia a una tabla y luego, al lado de las referencias, tenemos que dar la tabla como referencia y entre paréntesis dar el nombre de la columna de la tabla a la que se hace referencia, generalmente las claves foráneas se dan como columnas de clave principal.

CASO 2: si desea una clave externa para una tabla existente en una columna existente

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

NOTA: los corchetes '()' después de FOREIGN KEY y REFERENCES tabel2 son obligatorios o, de lo contrario, postgres arrojará un error.

Ashok Allu
fuente
0

Yo se el problema Los nombres de las columnas son diferentes. Tal vez en una columna, hay un espacio adicional después del nombre de la columna, así que asegúrese de que los nombres de las columnas tengan el mismo nombre.

XIN WANG
fuente
1
OP preguntó: ¿Esta declaración no crea la columna también? Por lo tanto, es evidente que esperaba que eso sucediera.
Laurenz Albe