Jak utworzyć klucz obcy w SQL Server?

243

Nigdy nie „ręcznie kodowałem” kodu do tworzenia obiektów dla SQL Server, a decleracja klucza obcego jest pozornie różna dla SQL Server i Postgres. Oto mój SQL jak dotąd:

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

Po uruchomieniu zapytania pojawia się następujący błąd:

Msg 8139, poziom 16, stan 0, wiersz 9 Liczba kolumn referencyjnych w kluczu obcym różni się od liczby kolumn referencyjnych, tabela „bank pytań”.

Czy widzisz błąd?

mmattax
źródło
2
Do Twojej wiadomości, zawsze najlepiej nazwać swoje ograniczenia, szczególnie przy użyciu ORM.
Tracker1

Odpowiedzi:

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
źródło
37
Pomocne może być również nazwanie ograniczenia klucza obcego. Pomaga to w rozwiązywaniu problemów dotyczących naruszeń fk. Na przykład: „klucz obcy fk_questionbank_exams (pytanie_exam_id) odwołuje się do egzaminów (egzamin_id)”
John Vasileff
31
I zgadzam nazywania ograniczeń jest dobry plan, ale w SQL Server 2008 R2 przynajmniej składnia ostatniej linii musi być „ograniczenie fk_questionbank_exams klucz obcy (question_exam_id) odniesienia egzaminów (exam_id)”
Jonathan Sayce
5
Bardzo ważny punkt, na który należy zwrócić uwagę, że utworzenie klucza obcego nie tworzy indeksu. Dołączenie innej tabeli do tej może spowodować bardzo wolne zapytanie.
Rocklan
Nie jestem pewien, dlaczego jest inaczej, ale musiałem wykonać KONTRAKT fk_questionbank_exams KLUCZ ZAGRANICZNY (pytanie_exam_id) REFERENCJE egzaminy (egzamin_id)
tenmiles
Czy konieczne jest napisanie NON NULL dla klucza podstawowego, czy jest to wyraźne, gdy piszemy ograniczenie klucza podstawowego dla kolumny, np. Siedzę na tyle, aby oznaczyć kolumnę jako klucz podstawowy, aby mieć ograniczenie inne niż null lub musi należy podać NON NULL, tj. napisać wprost?
Gary
326

A jeśli chcesz utworzyć ograniczenie samodzielnie, możesz użyć ALTER TABLE

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

Nie polecałbym składni wspomnianej przez Sarę Chipps do tworzenia w tekście, tylko dlatego, że wolałbym nazywać własne ograniczenia.

AlexCuse
źródło
19
Wiem, że to jest bardzo stare ... ale dostałem się tutaj z wyszukiwarki Google i tylu innych mogło. Szybka poprawka: poprawny sposób na odniesienie to: REFERENCJE MyOtherTable (MyOtherIDColumn)
PedroC88
3
MyTable_MyColumn_FK to najlepsza praktyka nazewnictwa.
shaijut
70

Możesz także nazwać swoje ograniczenie klucza obcego, używając:

CONSTRAINT your_name_here FOREIGN KEY (question_exam_id) REFERENCES EXAMS (exam_id)
Sara Chipps
źródło
1
Korzystając z ORM, pomocne jest nazwanie ograniczeń z wieloma odniesieniami do tabeli obcej ... Użyłem nazwanych ograniczeń we właściwościach z EF4, dzięki czemu wiedziałem, który wpis tabeli kontaktów jest przeznaczony dla kupującego, sprzedającego itp.
Tracker1
31

Podoba mi się odpowiedź AlexCuse, ale na coś, na co powinieneś zwrócić uwagę, gdy dodajesz ograniczenie klucza obcego, to sposób, w jaki chcesz, aby aktualizacje do referencyjnej kolumny w wierszu odnośnej tabeli były traktowane, a zwłaszcza jak chcesz usunąć wiersze w odnośniku stół do leczenia.

Jeśli ograniczenie zostanie utworzone w ten sposób:

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

.. wówczas aktualizacja lub usuwanie w tabeli odniesienia spowoduje wysadzenie z błędem, jeśli w tabeli odniesienia znajduje się odpowiedni wiersz.

To może być zachowanie, które chcesz, ale z mojego doświadczenia wynika, że ​​znacznie częściej nie jest.

Jeśli zamiast tego utworzysz go w ten sposób:

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

... następnie aktualizacje i usuwanie w tabeli nadrzędnej spowodują aktualizacje i usuwanie odpowiednich wierszy w tabeli odwołań.

(Nie sugeruję, że domyślna powinna zostać zmieniona, domyślna pomyłka jest po stronie ostrożności, co jest dobre. Po prostu mówię, że jest to coś, na co osoba, która tworzy conststainty, powinna zawsze zwracać uwagę .)

Nawiasem mówiąc, można to zrobić, tworząc tabelę:

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
źródło
Działa lepiej z „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
);

- To też zadziała. Może nieco bardziej intuicyjna konstrukcja?

Bijimon
źródło
1
Tak robię, ale mam pytanie: czy warto dodawać słowa kluczowe „klucz obcy”? - wydaje się, że działa bez tego, np .: pytanie_exam_id unikatowy identyfikator nie zerowe referencje egzaminy (egzamin_id)
JSideris
Słowa kluczowe „klucz obcy” są opcjonalne. W mojej opinii kod ten jest bardziej czytelny.
Bijimon
8

Aby utworzyć klucz obcy w dowolnej tabeli

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
źródło
8

Jeśli chcesz utworzyć dwie kolumny tabeli w relacji za pomocą zapytania, spróbuj wykonać następujące czynności:

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
źródło
5

Tak jak ty, zwykle nie tworzę obcych kluczy ręcznie, ale jeśli z jakiegoś powodu potrzebuję skryptu, to zwykle tworzę go za pomocą studia zarządzania serwerem ms sql i przed zapisaniem zmian, wybieram Projektant tabel | Wygeneruj skrypt zmian

Vitor Silva
źródło
4

Ten skrypt dotyczy tworzenia tabel z kluczem obcym i dodałem ograniczenie integralności referencyjnej dla serwera sql .

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
źródło
3

Nekromancja.
Właściwie robienie tego poprawnie jest nieco trudniejsze.

Najpierw musisz sprawdzić, czy istnieje klucz podstawowy dla kolumny, do której chcesz ustawić swój klucz obcy.

W tym przykładzie tworzony jest klucz obcy w tabeli T_ZO_SYS_Language_Forms, odwołujący się do 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
źródło
2

Zawsze używam tej składni do tworzenia ograniczenia klucza obcego między 2 tabelami

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

to znaczy

Alter Table tblEmployee
Add constraint tblEmployee_DepartmentID_FK
foreign key (DepartmentID) references tblDepartment (ID)
Aamir Shaikh
źródło