Comment créer une clé étrangère dans SQL Server?

243

Je n'ai jamais de code de création d'objet "codé à la main" pour SQL Server et le traitement de clé étrangère est apparemment différent entre SQL Server et Postgres. Voici mon sql jusqu'à présent:

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
);

Lorsque j'exécute la requête, j'obtiens cette erreur:

Msg 8139, niveau 16, état 0, ligne 9 Le nombre de colonnes de référence dans la clé étrangère diffère du nombre de colonnes référencées, table 'banque_questions'.

Pouvez-vous repérer l'erreur?

mmattax
la source
2
Pour info, il est toujours préférable de nommer vos contraintes, en particulier avec les ORM en cours d'utilisation.
Tracker1

Réponses:

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
la source
37
Il peut également être utile de nommer la contrainte de clé étrangère. Cela aide à résoudre les violations de fk. Par exemple: "la clé étrangère fk_questionbank_exams (question_exam_id) fait référence aux examens (exam_id)"
John Vasileff
31
Je suis d'accord pour nommer les contraintes est un bon plan mais, dans SQL Server 2008 R2 au moins, la syntaxe de la dernière ligne doit être "contrainte fk_questionbank_exams clé étrangère (question_exam_id) référence aux examens (exam_id)"
Jonathan Sayce
5
Un point très important à noter que la création de la clé étrangère ne crée pas d'index. Joindre une autre table à celle-ci peut entraîner une requête extrêmement lente.
Rocklan
Je ne sais pas pourquoi c'est différent, mais j'ai dû faire CONSTRAINT fk_questionbank_exams FOREIGN KEY (question_exam_id) RÉFÉRENCES examens (exam_id)
tenmiles
Est-il nécessaire d'écrire un NON NULL pour la clé primaire, ou est-ce explicite lorsque nous écrivons la contrainte de clé primaire pour la colonne, par exemple, je suis assez assis pour désigner une colonne comme clé primaire pour avoir une contrainte non nulle ou doit le NON NULL soit spécifié, c'est-à-dire écrit explicitement?
gary
326

Et si vous voulez simplement créer la contrainte seule, vous pouvez utiliser ALTER TABLE

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

Je ne recommanderais pas la syntaxe mentionnée par Sara Chipps pour la création en ligne, juste parce que je préfère nommer mes propres contraintes.

AlexCuse
la source
19
Je sais que c'est vieux ... mais je suis arrivé ici à partir d'une recherche Google et tant d'autres pourraient. Juste une solution rapide: la bonne façon de faire référence est: RÉFÉRENCES MyOtherTable (MyOtherIDColumn)
PedroC88
3
MyTable_MyColumn_FK est la meilleure pratique de dénomination.
shaijut
70

Vous pouvez également nommer votre contrainte de clé étrangère en utilisant:

CONSTRAINT your_name_here FOREIGN KEY (question_exam_id) REFERENCES EXAMS (exam_id)
Sara Chipps
la source
1
Lorsque vous utilisez un ORM, il est utile d'avoir des contraintes nommées avec plusieurs références à la table étrangère ... Utilisé des contraintes nommées dans les propriétés avec EF4, afin que je sache quelle entrée de la table de contacts était pour l'acheteur, le vendeur, etc.
Tracker1
31

J'aime la réponse d'AlexCuse, mais une chose à laquelle vous devez faire attention chaque fois que vous ajoutez une contrainte de clé étrangère est de savoir comment vous souhaitez que les mises à jour de la colonne référencée dans une ligne de la table référencée soient traitées, et surtout comment vous souhaitez supprimer les lignes dans la référence table à traiter.

Si une contrainte est créée comme ceci:

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

.. puis les mises à jour ou les suppressions dans la table référencée exploseront avec une erreur s'il y a une ligne correspondante dans la table référencée.

C'est peut-être le comportement que vous voulez, mais d'après mon expérience, ce n'est généralement pas le cas.

Si vous le créez à la place comme ceci:

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

..alors les mises à jour et suppressions dans la table parent entraîneront des mises à jour et des suppressions des lignes correspondantes dans la table de référence.

(Je ne suggère pas que la valeur par défaut devrait être modifiée, la valeur par défaut est de la prudence, ce qui est bien. Je dis simplement que c'est quelque chose qu'une personne qui crée des contraintes doit toujours faire attention .)

Cela peut être fait, en passant, lors de la création d'une table, comme ceci:

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
la source
Fonctionne mieux avec "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
);

- Cela fonctionnera aussi. Peut-être une construction un peu plus intuitive?

Bijimon
la source
1
C'est ce que je fais, mais j'ai une question, est-il utile d'ajouter les mots clés "clé étrangère"? - il semble fonctionner sans cela, par exemple: question_exam_id uniqueidentifier pas des références nulles aux examens (exam_id)
JSideris
Les mots clés "clé étrangère" sont facultatifs. À mon avis, cela rend le code plus lisible.
Bijimon
8

Pour créer une clé étrangère sur n'importe quelle table

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
la source
8

Si vous souhaitez créer deux colonnes de table dans une relation à l'aide d'une requête, essayez ce qui suit:

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
la source
5

Comme vous, je ne crée généralement pas de clés étrangères à la main, mais si pour une raison quelconque j'ai besoin du script, je le crée généralement à l'aide de ms sql server management studio et avant d'enregistrer puis de modifier, je sélectionne Table Designer | Générer un script de changement

Vitor Silva
la source
4

Ce script concerne la création de tables avec une clé étrangère et j'ai ajouté la contrainte d'intégrité référentielle 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
la source
3

Nécromancement.
En fait, le faire correctement est un peu plus délicat.

Vous devez d'abord vérifier si la clé primaire existe pour la colonne à laquelle vous souhaitez définir votre clé étrangère comme référence.

Dans cet exemple, une clé étrangère sur la table T_ZO_SYS_Language_Forms est créée, référençant 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
la source
2

J'utilise toujours cette syntaxe pour créer la contrainte de clé étrangère entre 2 tables

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

c'est à dire

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