Come posso creare una chiave esterna in SQL Server?


243

Non ho mai "codificato manualmente" il codice di creazione di oggetti per SQL Server e la declerazione di chiavi esterne è apparentemente diversa tra SQL Server e Postgres. Ecco il mio sql finora:

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

Quando eseguo la query ottengo questo errore:

Messaggio 8139, livello 16, stato 0, riga 9 Il numero di colonne di riferimento in chiave esterna differisce dal numero di colonne di riferimento, tabella "question_bank".

Riesci a individuare l'errore?


2
Cordiali saluti, sempre meglio per nominare i tuoi vincoli, soprattutto con ORM in uso.
Tracker 1

Risposte:


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

37
Può anche essere utile nominare il vincolo di chiave esterna. Questo aiuta a risolvere le violazioni di fk. Ad esempio: "chiave esterna fk_questionbank_exams (question_exam_id) riferimenti esami (exam_id)"
John Vasileff,

31
Sono d'accordo che i vincoli di denominazione sono un buon piano ma, almeno in SQL Server 2008 R2, la sintassi dell'ultima riga deve essere "vincolo fk_questionbank_exams chiave esterna (question_exam_id) riferimenti esami (exam_id)"
Jonathan Sayce

5
Un punto molto importante da notare che la creazione della chiave esterna non crea un indice. Unire un'altra tabella a questa potrebbe comportare una query estremamente lenta.
Rocklan,

Non sono sicuro del perché sia ​​diverso, ma ho dovuto fare CONSTRAINT fk_questionbank_exams FOREIGN KEY (question_exam_id) RIFERIMENTI esami (exam_id)
tenmiles

È necessario scrivere un NON NULL per la chiave primaria o è esplicito quando si scrive il vincolo della chiave primaria per la colonna, ad esempio, mi siedo abbastanza per indicare una colonna come chiave primaria per avere un vincolo non nullo o deve il NON NULL deve essere specificato, cioè scritto esplicitamente?
gary

326

E se vuoi solo creare il vincolo da solo, puoi usare ALTER TABLE

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

Non consiglierei la sintassi menzionata da Sara Chipps per la creazione in linea, solo perché preferirei nominare i miei vincoli.


19
So che è vecchio ... ma sono arrivato da una ricerca su Google e così tanti altri potrebbero. Solo una soluzione rapida: il modo corretto di fare riferimento è: RIFERIMENTI MyOtherTable (MyOtherIDColumn)
PedroC88

3
MyTable_MyColumn_FK è la migliore pratica di denominazione.
Shaijut

70

È inoltre possibile assegnare un nome al vincolo di chiave esterna utilizzando:

CONSTRAINT your_name_here FOREIGN KEY (question_exam_id) REFERENCES EXAMS (exam_id)

1
Quando si utilizza un ORM, è utile disporre di vincoli con nome con più riferimenti alla tabella esterna ... Utilizzato vincoli con nome nelle proprietà con EF4, in modo che sapessi quale voce della tabella di contatto era per acquirente, venditore, ecc.
Tracker1

31

Mi piace la risposta di AlexCuse, ma qualcosa a cui dovresti prestare attenzione ogni volta che aggiungi un vincolo di chiave esterna è il modo in cui vuoi che gli aggiornamenti della colonna referenziata in una riga della tabella referenziata vengano trattati, e in particolare il modo in cui desideri eliminare le righe nel riferimento tavolo da trattare.

Se un vincolo viene creato in questo modo:

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

.. quindi gli aggiornamenti o le eliminazioni nella tabella referenziata esploderanno con un errore se c'è una riga corrispondente nella tabella referenziata.

Potrebbe essere il comportamento che desideri, ma nella mia esperienza, molto più comunemente non lo è.

Se invece lo crei in questo modo:

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

..quindi gli aggiornamenti e le eliminazioni nella tabella padre comporteranno aggiornamenti ed eliminazioni delle righe corrispondenti nella tabella di riferimento.

(Non sto suggerendo che l'impostazione predefinita debba essere modificata, gli errori predefiniti sul lato della cautela, il che è positivo. Sto solo dicendo che è qualcosa a cui una persona che crea conserve dovrebbe sempre prestare attenzione .)

Questo può essere fatto, tra l'altro, durante la creazione di una tabella, in questo modo:

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
)

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

- Funzionerà anche quello. Forse un costrutto un po 'più intuitivo?


1
Questo è quello che faccio, ma ho una domanda: c'è un punto di aggiunta delle parole chiave "chiave esterna"? - sembra funzionare senza questo, ad esempio: question_exam_id uniqueidentifier non null references esami (exam_id)
JSideris

Le parole chiave "Chiave esterna" sono facoltative. Nella mia opinione rende il codice più leggibile.
Bijimon,

8

Per creare una chiave esterna su qualsiasi tabella

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)

8

Se si desidera creare due colonne di una tabella in una relazione utilizzando una query, provare quanto segue:

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)

5

Come te, di solito non creo chiavi esterne a mano, ma se per qualche motivo ho bisogno dello script per farlo, di solito lo creo usando ms sql server management studio e prima di salvare e poi cambiare, seleziono Table Designer | Genera script di modifica


4

Questo script riguarda la creazione di tabelle con chiave esterna e ho aggiunto un vincolo di integrità referenziale 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
);

3

Necromancing.
In realtà, farlo correttamente è un po 'più complicato.

È innanzitutto necessario verificare se esiste la chiave primaria per la colonna a cui si desidera impostare la chiave esterna a cui fare riferimento.

In questo esempio, viene creata una chiave esterna nella tabella T_ZO_SYS_Language_Forms, facendo riferimento 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 

2

Uso sempre questa sintassi per creare il vincolo di chiave esterna tra 2 tabelle

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

vale a dire

Alter Table tblEmployee
Add constraint tblEmployee_DepartmentID_FK
foreign key (DepartmentID) references tblDepartment (ID)
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.