Làm cách nào để tạo khóa ngoại trong SQL Server?


243

Tôi chưa bao giờ mã hóa đối tượng "mã hóa" cho SQL Server và việc giải mã khóa ngoại có vẻ khác nhau giữa SQL Server và Postgres. Đây là sql của tôi cho đến nay:

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

Khi tôi chạy truy vấn, tôi gặp lỗi này:

Msg 8139, Cấp 16, Trạng thái 0, Dòng 9 Số cột tham chiếu trong khóa ngoại khác với số cột được tham chiếu, bảng 'question_bank'.

Bạn có thể nhận ra lỗi không?


2
FYI, luôn luôn tốt nhất để đặt tên cho các ràng buộc của bạn, đặc biệt là với các ORM đang sử dụng.
Tracker1

Câu trả lời:


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
Nó cũng có thể hữu ích để đặt tên cho ràng buộc khóa ngoại. Điều này giúp khắc phục sự cố vi phạm fk. Ví dụ: "khóa ngoại fk_questionbank_exams (question_exam_id) tài liệu tham khảo các kỳ thi (tests_id)"
John Vasileff

31
Tôi đồng ý các ràng buộc đặt tên là một kế hoạch tốt, nhưng ít nhất, trong SQL Server 2008 R2, cú pháp của dòng cuối cùng phải là "ràng buộc fk_questionbank_exams khóa ngoại lệ (question_exam_id) tham chiếu các kỳ thi (tests_id)"
Jonathan Sayce

5
Một điểm rất quan trọng cần lưu ý là việc tạo khóa ngoại không tạo chỉ mục. Tham gia một bảng khác vào bảng này có thể dẫn đến một truy vấn cực kỳ chậm.
Rocklan

Tôi không chắc tại sao nó lại khác, nhưng tôi đã phải thực hiện CONSTRAINT fk_questionbank_exams FOREIGN KEY (question_exam_id) TÀI LIỆU THAM KHẢO (tests_id)
tenmiles 29/07/2016

Có cần thiết phải viết NON NULL cho khóa chính không, hay điều này rõ ràng khi chúng ta viết ràng buộc khóa chính cho cột, ví dụ: tôi ngồi đủ để biểu thị một cột là khóa chính để có ràng buộc không null hoặc phải NON NULL được chỉ định, nghĩa là, được viết rõ ràng?
gary

326

Và nếu bạn chỉ muốn tự tạo ràng buộc, bạn có thể sử dụng ALTER TABLE

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

Tôi sẽ không đề xuất cú pháp được đề cập bởi Sara Chipps cho việc tạo nội tuyến, chỉ vì tôi muốn đặt tên cho các ràng buộc của riêng mình.


19
Tôi biết điều này đã chết ... nhưng tôi đã đến đây từ một tìm kiếm google và rất nhiều người khác có thể. Chỉ cần sửa chữa nhanh: cách chính xác để tham khảo là: TÀI LIỆU THAM KHẢO MyOtherTable (MyOtherIDColumn)
PedroC88

3
MyTable_MyColumn_FK là cách thực hành đặt tên tốt nhất.
shaijut

70

Bạn cũng có thể đặt tên cho ràng buộc khóa ngoại của mình bằng cách sử dụng:

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

1
Khi sử dụng ORM, sẽ rất hữu ích khi có các ràng buộc được đặt tên với nhiều tham chiếu đến bảng ngoại ... Đã sử dụng các ràng buộc có tên trong các thuộc tính với EF4, để tôi biết mục nhập bảng liên hệ nào dành cho người mua, người bán, v.v.
Tracker1

31

Tôi thích câu trả lời của AlexCuse, nhưng điều bạn nên chú ý mỗi khi bạn thêm một ràng buộc khóa ngoại là cách bạn muốn cập nhật cho cột được tham chiếu trong một hàng của bảng được tham chiếu và đặc biệt là cách bạn muốn xóa các hàng trong tham chiếu bảng được điều trị.

Nếu một ràng buộc được tạo như thế này:

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

.. sau đó cập nhật hoặc xóa trong bảng được tham chiếu sẽ xuất hiện lỗi nếu có một hàng tương ứng trong bảng tham chiếu.

Đó có thể là hành vi bạn muốn, nhưng theo kinh nghiệm của tôi, nó phổ biến hơn nhiều.

Nếu bạn thay vì tạo nó như thế này:

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

.. sau đó cập nhật và xóa trong bảng cha sẽ dẫn đến cập nhật và xóa các hàng tương ứng trong bảng tham chiếu.

(Tôi không gợi ý rằng nên thay đổi mặc định, lỗi mặc định ở khía cạnh thận trọng, điều này tốt. Tôi chỉ nói đó là điều mà một người đang tạo ra các chòm sao nên luôn luôn chú ý đến .)

Điều này có thể được thực hiện, bằng cách này, khi tạo một bảng, như thế này:

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
)

Hoạt động tốt hơn với "bảng thay đổi MyTable (...)". :)
Sylvain Coleue

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

- Điều đó cũng sẽ làm việc. Pehaps xây dựng trực quan hơn một chút?


1
Đây là những gì tôi làm, nhưng tôi có một câu hỏi, Có điểm nào thêm từ khóa "khóa ngoại" không? - nó có vẻ làm việc mà không có, ví dụ như: question_exam_id uniqueidentifier không null tài liệu tham khảo kỳ thi (exam_id)
JSideris

Các từ khóa "Khóa ngoại" là tùy chọn. Theo ý kiến ​​của tôi, nó làm cho mã dễ đọc hơn.
Bijimon

8

Để tạo khóa ngoại trên bất kỳ bảng nào

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

Nếu bạn muốn tạo hai cột của bảng thành mối quan hệ bằng cách sử dụng truy vấn, hãy thử như sau:

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

Giống như bạn, tôi thường không tạo khóa ngoại bằng tay, nhưng vì lý do nào đó tôi cần tập lệnh để thực hiện nên tôi thường tạo nó bằng studio quản lý máy chủ ms sql và trước khi lưu rồi thay đổi, tôi chọn Trình thiết kế bảng | Tạo tập lệnh thay đổi


4

Kịch bản này là về việc tạo các bảng có khóa ngoại và tôi đã thêm máy chủ sql ràng buộc toàn vẹn tham chiếu .

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

Necromance.
Trên thực tế, làm điều này một cách chính xác là một chút khó khăn hơn.

Trước tiên bạn cần kiểm tra xem khóa chính có tồn tại cho cột bạn muốn đặt khóa ngoại của bạn để tham chiếu không.

Trong ví dụ này, một khóa ngoại trên bảng T_ZO_SYS_L Language_Forms được tạo, tham chiếu dbo.T_SYS_L 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

Tôi luôn sử dụng cú pháp này để tạo ràng buộc khóa ngoài giữa 2 bảng

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

I E

Alter Table tblEmployee
Add constraint tblEmployee_DepartmentID_FK
foreign key (DepartmentID) references tblDepartment (ID)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.