ฉันจะสร้าง foreign key ใน SQL Server ได้อย่างไร


243

ฉันไม่เคยมีรหัสการสร้างออบเจ็กต์แบบ "เข้ารหัสด้วยมือ" สำหรับ SQL Server และการถอดรหัสคีย์ต่างประเทศดูเหมือนจะแตกต่างกันระหว่าง SQL Server และ Postgres นี่คือ sql ของฉันจนถึงตอนนี้:

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

เมื่อฉันเรียกใช้แบบสอบถามฉันได้รับข้อผิดพลาดนี้:

ข่าวสารเกี่ยวกับ 8139, ระดับ 16, สถานะ 0, สาย 9 จำนวนคอลัมน์อ้างอิงใน foreign key จะแตกต่างจากจำนวนคอลัมน์อ้างอิง, ตาราง 'question_bank'

คุณเห็นข้อผิดพลาดได้หรือไม่?


2
FYI ดีที่สุดเสมอในการตั้งชื่อข้อ จำกัด ของคุณโดยเฉพาะอย่างยิ่งกับ ORM ที่ใช้งานอยู่
Tracker1

คำตอบ:


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
นอกจากนี้ยังเป็นประโยชน์ในการตั้งชื่อข้อ จำกัด คีย์ต่างประเทศ ซึ่งจะช่วยแก้ไขปัญหาการละเมิด fk ตัวอย่างเช่น: "foreign key fk_questionbank_exams (question_exam_id) การสอบอ้างอิง (exam_id)"
John Vasileff

31
ฉันยอมรับข้อ จำกัด ในการตั้งชื่อเป็นแผนการที่ดี แต่อย่างน้อยใน SQL Server 2008 R2 ไวยากรณ์ของบรรทัดสุดท้ายจะต้องเป็น "ข้อ จำกัด fk_questionbank_exams foreign key (question_exam_id) การสอบอ้างอิง (exam_id)"
Jonathan Sayce

5
จุดสำคัญมากที่ควรทราบว่าการสร้างคีย์ต่างประเทศนั้นไม่ได้สร้างดัชนี การเข้าร่วมตารางอื่นในตารางนี้อาจทำให้การสืบค้นช้ามาก
Rocklan

ฉันไม่แน่ใจว่าทำไมถึงแตกต่างกัน แต่ฉันต้องทำ CONSTRAINT fk_questionbank_exams คีย์ต่างประเทศ (question_exam_id) การสอบอ้างอิง (exam_id)
tenmiles

จำเป็นหรือไม่ในการเขียน NON NULL สำหรับคีย์หลักหรือชัดเจนเมื่อเราเขียนข้อ จำกัด คีย์หลักสำหรับคอลัมน์เช่นฉันนั่งเพียงพอที่จะแสดงคอลัมน์เป็นคีย์หลักเพื่อให้มีข้อ จำกัด ที่ไม่เป็นโมฆะหรือต้อง ไม่ใช่ NULL ที่ระบุเช่นเขียนอย่างชัดเจน?
แกรี่

326

และหากคุณต้องการสร้างข้อ จำกัด ด้วยตัวเองคุณสามารถใช้ ALTER TABLE

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

ฉันจะไม่แนะนำไวยากรณ์ที่ Sara Chipps พูดถึงสำหรับการสร้างแบบอินไลน์เพียงเพราะฉันต้องการตั้งชื่อข้อ จำกัด ของตัวเอง


19
ฉันรู้ว่านี่เก่าแก่แล้ว ... แต่ฉันได้มาจากการค้นหาด้วยกูเกิ้ลและคนอื่น ๆ ก็ทำได้ เพียงแค่การแก้ไขอย่างรวดเร็ว: วิธีอ้างอิงที่ถูกต้องคือ: การอ้างอิง MyOtherTable (MyOtherIDColumn)
PedroC88

3
MyTable_MyColumn_FKเป็นแนวปฏิบัติที่ดีที่สุดในการตั้งชื่อ
shaijut

70

คุณสามารถตั้งชื่อข้อ จำกัด กุญแจต่างประเทศของคุณโดยใช้:

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

1
เมื่อใช้ ORM จะช่วยให้มีการตั้งชื่อข้อ จำกัด ด้วยการอ้างอิงหลายอย่างไปยังตารางต่างประเทศ ... ใช้ข้อ จำกัด ที่มีชื่อในคุณสมบัติกับ EF4 เพื่อที่ฉันจะได้รู้ว่ารายการตารางติดต่อใดเหมาะสำหรับผู้ซื้อผู้ขาย ฯลฯ
Tracker1

31

ฉันชอบคำตอบของ AlexCuse แต่สิ่งที่คุณควรใส่ใจเมื่อใดก็ตามที่คุณเพิ่มข้อ จำกัด คีย์ต่างประเทศคือวิธีที่คุณต้องการอัปเดตคอลัมน์อ้างอิงในแถวของตารางอ้างอิงที่จะได้รับการปฏิบัติและโดยเฉพาะอย่างยิ่งวิธีที่คุณต้องการลบแถวในการอ้างอิง ตารางที่จะได้รับการปฏิบัติ

หากมีข้อ จำกัด เกิดขึ้นเช่นนี้:

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

.. จากนั้นการอัปเดตหรือลบในตารางที่อ้างอิงจะเกิดข้อผิดพลาดหากมีแถวที่เกี่ยวข้องในตารางอ้างอิง

นั่นอาจเป็นพฤติกรรมที่คุณต้องการ แต่จากประสบการณ์ของฉัน

หากคุณสร้างแบบนี้แทน:

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

.. จากนั้นการอัปเดตและลบในตารางหลักจะส่งผลให้เกิดการอัปเดตและลบแถวที่เกี่ยวข้องในตารางอ้างอิง

(ฉันไม่ได้บอกว่าควรเปลี่ยนค่าเริ่มต้นผิดพลาดทางด้านความระมัดระวังซึ่งเป็นเรื่องดีฉันแค่บอกว่ามันเป็นสิ่งที่คนที่สร้างกลุ่มคนควรให้ความสนใจเสมอ)

สิ่งนี้สามารถทำได้โดยวิธีการเมื่อสร้างตารางเช่นนี้

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
)

ทำงานได้ดีขึ้นด้วย "แก้ไขตาราง 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
);

- นั่นจะทำงานเช่นกัน อาจจะสร้างง่ายขึ้นอีกหน่อย


1
นี่คือสิ่งที่ฉันทำ แต่ฉันมีคำถามมีจุดเพิ่มคำสำคัญ "foreign key" หรือไม่? - ดูเหมือนว่าจะทำงานโดยไม่มีสิ่งนั้นเช่น: question_exam_id uniqueidentifier ไม่ใช่ข้อสอบอ้างอิงที่เป็นโมฆะ (exam_id)
JSideris

คำหลัก "คีย์ต่างประเทศ" เป็นทางเลือก ใน opionion ของฉันมันทำให้โค้ดอ่านง่ายขึ้น
Bijimon

8

เพื่อสร้างคีย์ต่างประเทศในตารางใด ๆ

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

หากคุณต้องการสร้างคอลัมน์ของสองตารางให้เป็นความสัมพันธ์โดยใช้แบบสอบถามลองทำดังนี้:

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

เช่นเดียวกับคุณฉันมักจะไม่สร้างคีย์ต่างประเทศด้วยมือ แต่ถ้าด้วยเหตุผลบางอย่างฉันต้องการสคริปต์เพื่อทำเช่นนั้นฉันมักจะสร้างมันขึ้นมาโดยใช้สตูดิโอการจัดการเซิร์ฟเวอร์ของ sql sql และก่อนที่จะบันทึก สร้างสคริปต์การเปลี่ยนแปลง


4

สคริปต์นี้เป็นเรื่องเกี่ยวกับการสร้างตารางที่มีคีย์ต่างประเทศและฉันเพิ่มอ้างอิงสมบูรณ์ จำกัด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
);

3

Necromancing
ที่จริงแล้วการทำอย่างถูกต้องเป็นเรื่องยุ่งยากเล็กน้อย

ก่อนอื่นคุณต้องตรวจสอบว่ามีคีย์หลักอยู่ในคอลัมน์ที่คุณต้องการตั้งค่า foreign key ของคุณให้อ้างอิงหรือไม่

ในตัวอย่างนี้คีย์ต่างประเทศในตาราง T_ZO_SYS_Language_Forms ถูกสร้างขึ้นโดยอ้างอิง 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

ฉันมักจะใช้ไวยากรณ์นี้เพื่อสร้างข้อ จำกัด foreign key ระหว่าง 2 ตาราง

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

กล่าวคือ

Alter Table tblEmployee
Add constraint tblEmployee_DepartmentID_FK
foreign key (DepartmentID) references tblDepartment (ID)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.