¿Cómo creo una clave foránea en SQL Server?

243

Nunca he "codificado a mano" el código de creación de objetos para SQL Server y la declinación de la clave externa es aparentemente diferente entre SQL Server y Postgres. Aquí está mi sql hasta ahora:

drop table exams;
drop table question_bank;
drop table anwser_bank;

create table exams
(
    exam_id uniqueidentifier primary key,
    exam_name varchar(50),
);
create table question_bank
(
    question_id uniqueidentifier primary key,
    question_exam_id uniqueidentifier not null,
    question_text varchar(1024) not null,
    question_point_value decimal,
    constraint question_exam_id foreign key references exams(exam_id)
);
create table anwser_bank
(
    anwser_id           uniqueidentifier primary key,
    anwser_question_id  uniqueidentifier,
    anwser_text         varchar(1024),
    anwser_is_correct   bit
);

Cuando ejecuto la consulta me sale este error:

Mensaje 8139, Nivel 16, Estado 0, Línea 9 El número de columnas de referencia en clave externa difiere del número de columnas referenciadas, tabla 'question_bank'.

¿Puedes detectar el error?

mmattax
fuente
2
Para su información, siempre es mejor nombrar sus restricciones, especialmente con ORM en uso.
Rastreador1

Respuestas:

198
create table question_bank
(
    question_id uniqueidentifier primary key,
    question_exam_id uniqueidentifier not null,
    question_text varchar(1024) not null,
    question_point_value decimal,
    constraint fk_questionbank_exams foreign key (question_exam_id) references exams (exam_id)
);
John Boker
fuente
37
También puede ser útil nombrar la restricción de clave externa. Esto ayuda a solucionar las infracciones de fk. Por ejemplo: "clave extranjera fk_questionbank_exams (question_exam_id) referencias exámenes (exam_id)"
John Vasileff
31
Estoy de acuerdo en que nombrar restricciones es un buen plan, pero, al menos en SQL Server 2008 R2, la sintaxis de la última línea debe ser "restricción fk_questionbank_exams exámenes de referencias de clave externa (question_exam_id) referencias (exam_id)"
Jonathan Sayce
55
Un punto muy importante a tener en cuenta que la creación de la clave externa no crea un índice. Unir otra tabla a esta podría resultar en una consulta extremadamente lenta.
Rocklan
No estoy seguro de por qué es diferente, pero tuve que hacer CONSTRAINT fk_questionbank_exams FOREIGN KEY (question_exam_id) REFERENCES exámenes (exam_id)
tenmiles
¿Es necesario escribir un NON NULL para la clave primaria, o es explícito cuando escribimos la restricción de la clave primaria para la columna, por ejemplo, me siento lo suficiente como para denotar una columna como la clave primaria para tener una restricción no nula o debe se especifica NON NULL, es decir, ¿se escribe explícitamente?
Gary
326

Y si solo desea crear la restricción por sí solo, puede usar ALTER TABLE

alter table MyTable
add constraint MyTable_MyColumn_FK FOREIGN KEY ( MyColumn ) references MyOtherTable(PKColumn)

No recomendaría la sintaxis mencionada por Sara Chipps para la creación en línea, solo porque prefiero nombrar mis propias restricciones.

AlexCuse
fuente
19
Sé que esto es muy viejo ... pero llegué aquí de una búsqueda en Google y muchos otros pudieron. Solo una solución rápida: la forma correcta de referencia es: REFERENCIAS MyOtherTable (MyOtherIDColumn)
PedroC88
3
MyTable_MyColumn_FK es la mejor práctica de nomenclatura.
shaijut
70

También puede nombrar su restricción de clave externa utilizando:

CONSTRAINT your_name_here FOREIGN KEY (question_exam_id) REFERENCES EXAMS (exam_id)
Sara Chipps
fuente
1
Al usar un ORM, es útil tener restricciones con nombre con múltiples referencias a la tabla extranjera ... Usé restricciones con nombre en propiedades con EF4, para que supiera qué entrada de la tabla de contactos era para comprador, vendedor, etc.
Rastreador1
31

Me gusta la respuesta de AlexCuse, pero algo a lo que debe prestar atención cada vez que agrega una restricción de clave externa es cómo desea que se traten las actualizaciones de la columna referenciada en una fila de la tabla referenciada, y especialmente cómo desea eliminar las filas en la referencia mesa a tratar.

Si se crea una restricción como esta:

alter table MyTable
add constraint MyTable_MyColumn_FK FOREIGN KEY ( MyColumn ) 
references MyOtherTable(PKColumn)

.. luego las actualizaciones o eliminaciones en la tabla referenciada explotarán con un error si hay una fila correspondiente en la tabla de referencia.

Ese podría ser el comportamiento que desea, pero en mi experiencia, mucho más comúnmente no lo es.

Si en cambio lo creas así:

alter table MyTable
add constraint MyTable_MyColumn_FK FOREIGN KEY ( MyColumn ) 
references MyOtherTable(PKColumn)
on update cascade 
on delete cascade

.. luego las actualizaciones y eliminaciones en la tabla principal darán lugar a actualizaciones y eliminaciones de las filas correspondientes en la tabla de referencia.

(No estoy sugiriendo que se cambie el valor predeterminado, el error por defecto es cauteloso, lo cual es bueno. Solo digo que es algo a lo que una persona que crea constaints siempre debe prestar atención ).

Esto se puede hacer, por cierto, al crear una tabla, así:

create table ProductCategories (
  Id           int identity primary key,
  ProductId    int references Products(Id)
               on update cascade on delete cascade
  CategoryId   int references Categories(Id) 
               on update cascade on delete cascade
)
Shavais
fuente
Funciona mejor con "alter table MyTable (...)". :)
Sylvain Rodrigue
14
create table question_bank
(
    question_id uniqueidentifier primary key,
    question_exam_id uniqueidentifier not null constraint fk_exam_id foreign key references exams(exam_id),
    question_text varchar(1024) not null,
    question_point_value decimal
);

- Eso también funcionará. ¿Quizás una construcción un poco más intuitiva?

Bijimon
fuente
1
Esto es lo que hago, pero tengo una pregunta: ¿hay algún punto para agregar las palabras clave de "clave externa"? - parece funcionar sin eso, por ejemplo: question_exam_id uniqueidentifier no exámenes de referencias nulas (exam_id)
JSideris
Las palabras clave "Clave externa" son opcionales. En mi opinión, hace que el código sea más legible.
Bijimon
8

Para crear una clave foránea en cualquier tabla

ALTER TABLE [SCHEMA].[TABLENAME] ADD FOREIGN KEY (COLUMNNAME) REFERENCES [TABLENAME](COLUMNNAME)
EXAMPLE
ALTER TABLE [dbo].[UserMaster] ADD FOREIGN KEY (City_Id) REFERENCES [dbo].[CityMaster](City_Id)
Abhishek Jaiswal
fuente
8

Si desea crear dos columnas de tabla en una relación mediante una consulta, intente lo siguiente:

Alter table Foreign_Key_Table_name add constraint 
Foreign_Key_Table_name_Columnname_FK
Foreign Key (Column_name) references 
Another_Table_name(Another_Table_Column_name)
Md Ashikul Islam
fuente
5

Como usted, generalmente no creo claves foráneas a mano, pero si por alguna razón necesito el script para hacerlo, generalmente lo creo usando ms sql server management studio y antes de guardar los cambios, selecciono Table Designer | Generar secuencia de comandos de cambio

Vitor Silva
fuente
4

Este script trata sobre la creación de tablas con clave externa y agregué la restricción de integridad referencial sql-server .

create table exams
(  
    exam_id int primary key,
    exam_name varchar(50),
);

create table question_bank 
(
    question_id int primary key,
    question_exam_id int not null,
    question_text varchar(1024) not null,
    question_point_value decimal,
    constraint question_exam_id_fk
       foreign key references exams(exam_id)
               ON DELETE CASCADE
);
elkhayari abderrazzak
fuente
3

Nigromancia
En realidad, hacer esto correctamente es un poco más complicado.

Primero debe verificar si la clave principal existe para la columna a la que desea establecer su clave externa para hacer referencia.

En este ejemplo, se crea una clave externa en la tabla T_ZO_SYS_Language_Forms, que hace referencia a dbo.T_SYS_Language_Forms.LANG_UID

-- First, chech if the table exists...
IF 0 < (
    SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES 
    WHERE TABLE_TYPE = 'BASE TABLE'
    AND TABLE_SCHEMA = 'dbo'
    AND TABLE_NAME = 'T_SYS_Language_Forms'
)
BEGIN
    -- Check for NULL values in the primary-key column
    IF 0 = (SELECT COUNT(*) FROM T_SYS_Language_Forms WHERE LANG_UID IS NULL)
    BEGIN
        ALTER TABLE T_SYS_Language_Forms ALTER COLUMN LANG_UID uniqueidentifier NOT NULL 

        -- No, don't drop, FK references might already exist...
        -- Drop PK if exists 
        -- ALTER TABLE T_SYS_Language_Forms DROP CONSTRAINT pk_constraint_name 
        --DECLARE @pkDropCommand nvarchar(1000) 
        --SET @pkDropCommand = N'ALTER TABLE T_SYS_Language_Forms DROP CONSTRAINT ' + QUOTENAME((SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS 
        --WHERE CONSTRAINT_TYPE = 'PRIMARY KEY' 
        --AND TABLE_SCHEMA = 'dbo' 
        --AND TABLE_NAME = 'T_SYS_Language_Forms' 
        ----AND CONSTRAINT_NAME = 'PK_T_SYS_Language_Forms' 
        --))
        ---- PRINT @pkDropCommand 
        --EXECUTE(@pkDropCommand) 

        -- Instead do
        -- EXEC sp_rename 'dbo.T_SYS_Language_Forms.PK_T_SYS_Language_Forms1234565', 'PK_T_SYS_Language_Forms';


        -- Check if they keys are unique (it is very possible they might not be) 
        IF 1 >= (SELECT TOP 1 COUNT(*) AS cnt FROM T_SYS_Language_Forms GROUP BY LANG_UID ORDER BY cnt DESC)
        BEGIN

            -- If no Primary key for this table
            IF 0 =  
            (
                SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS 
                WHERE CONSTRAINT_TYPE = 'PRIMARY KEY' 
                AND TABLE_SCHEMA = 'dbo' 
                AND TABLE_NAME = 'T_SYS_Language_Forms' 
                -- AND CONSTRAINT_NAME = 'PK_T_SYS_Language_Forms' 
            )
                ALTER TABLE T_SYS_Language_Forms ADD CONSTRAINT PK_T_SYS_Language_Forms PRIMARY KEY CLUSTERED (LANG_UID ASC)
            ;

            -- Adding foreign key
            IF 0 = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS WHERE CONSTRAINT_NAME = 'FK_T_ZO_SYS_Language_Forms_T_SYS_Language_Forms') 
                ALTER TABLE T_ZO_SYS_Language_Forms WITH NOCHECK ADD CONSTRAINT FK_T_ZO_SYS_Language_Forms_T_SYS_Language_Forms FOREIGN KEY(ZOLANG_LANG_UID) REFERENCES T_SYS_Language_Forms(LANG_UID); 
        END -- End uniqueness check
        ELSE
            PRINT 'FSCK, this column has duplicate keys, and can thus not be changed to primary key...' 
    END -- End NULL check
    ELSE
        PRINT 'FSCK, need to figure out how to update NULL value(s)...' 
END 
Stefan Steiger
fuente
2

Siempre uso esta sintaxis para crear la restricción de clave externa entre 2 tablas

Alter Table ForeignKeyTable
Add constraint `ForeignKeyTable_ForeignKeyColumn_FK`
`Foreign key (ForeignKeyColumn)` references `PrimaryKeyTable (PrimaryKeyColumn)`

es decir

Alter Table tblEmployee
Add constraint tblEmployee_DepartmentID_FK
foreign key (DepartmentID) references tblDepartment (ID)
Aamir Shaikh
fuente