คอลัมน์ตารางที่มี Foreign Key เป็นโมฆะได้หรือไม่


247

ฉันมีตารางที่มีหลายคอลัมน์ ID ไปยังตารางอื่น ๆ

ฉันต้องการคีย์ต่างประเทศเพื่อบังคับใช้ความสมบูรณ์ก็ต่อเมื่อฉันใส่ข้อมูลลงในนั้น หากฉันทำการอัปเดตในภายหลังเพื่อเติมข้อมูลคอลัมน์นั้นควรตรวจสอบข้อ จำกัด ด้วย

(นี่อาจขึ้นอยู่กับเซิร์ฟเวอร์ฐานข้อมูลฉันใช้ประเภทตาราง MySQL & InnoDB)

ฉันเชื่อว่านี่เป็นความคาดหวังที่สมเหตุสมผล แต่โปรดแก้ไขฉันหากฉันผิด


6
ฉันไม่รู้เกี่ยวกับ MySQL แต่ MS SQL Server อนุญาตให้คีย์ต่างประเทศเป็นโมฆะด้วยความหมายที่คุณต้องการ ฉันคาดหวังว่านั่นคือพฤติกรรมมาตรฐาน
Jeffrey L Whitledge

1
คีย์ต่างประเทศไม่สามารถเป็นโมฆะโดยค่าเริ่มต้นใน mySQL เหตุผลก็ง่ายหากคุณอ้างอิงบางสิ่งและปล่อยให้เป็นโมฆะคุณจะสูญเสียความสมบูรณ์ของข้อมูล เมื่อคุณสร้างชุดตารางอนุญาตให้ null เป็น NOT จากนั้นใช้ข้อ จำกัด ของคีย์ต่างประเทศ คุณไม่สามารถตั้งค่า null ในการอัปเดตได้ควรส่งข้อผิดพลาดให้คุณ แต่คุณสามารถ (คุณต้อง) ไม่อัปเดตคอลัมน์นี้และอัปเดตเฉพาะฟิลด์ที่คุณต้องการเปลี่ยนแปลง
JoelBonetR

คำตอบ:


254

ได้คุณสามารถบังคับใช้ข้อ จำกัด ได้ก็ต่อเมื่อค่าไม่ใช่ NULL สามารถทดสอบได้ง่ายๆด้วยตัวอย่างต่อไปนี้:

CREATE DATABASE t;
USE t;

CREATE TABLE parent (id INT NOT NULL,
                     PRIMARY KEY (id)
) ENGINE=INNODB;

CREATE TABLE child (id INT NULL, 
                    parent_id INT NULL,
                    FOREIGN KEY (parent_id) REFERENCES parent(id)
) ENGINE=INNODB;


INSERT INTO child (id, parent_id) VALUES (1, NULL);
-- Query OK, 1 row affected (0.01 sec)


INSERT INTO child (id, parent_id) VALUES (2, 1);

-- ERROR 1452 (23000): Cannot add or update a child row: a foreign key 
-- constraint fails (`t/child`, CONSTRAINT `child_ibfk_1` FOREIGN KEY
-- (`parent_id`) REFERENCES `parent` (`id`))

เม็ดมีดแรกจะผ่านไปเพราะเราใส่ NULL ในไฟล์parent_id. การแทรกครั้งที่สองล้มเหลวเนื่องจากข้อ จำกัด ของ Foreign Key เนื่องจากเราพยายามแทรกค่าที่ไม่มีอยู่ในparentตาราง


16
ตารางพาเรนต์ยังสามารถประกาศด้วย id INT NOT NULL
จะ

@CJDennis ถ้าคุณสร้างเพื่อให้มีเพียงแถวเดียวเท่านั้นที่สามารถมี ID ว่างได้ก็สามารถใช้เป็นค่าสำรองสำหรับแถวอื่นได้ (แม้ว่าจะได้ผลดีกว่าสำหรับ DB หากคุณใช้คอลัมน์มากขึ้น) ข้อ จำกัด เริ่มต้นดูเหมือนจะเป็นปัญหาหากคุณต้องการทราบในภายหลังว่าค่าเดิมถูกตั้งเป็น "ค่าเริ่มต้น" (โดยใช้ null) หรือตั้งเป็น ค่าที่เกิดขึ้นจะเหมือนกับ "ค่าเริ่มต้น" ด้วยการมีแถวที่มีรหัสว่างคุณสามารถระบุได้อย่างชัดเจนว่าห้ามใช้แถวนี้เป็นแถวปกติและสามารถใช้แถวนี้เป็นวิธีการจัดเรียงค่าเริ่มต้นแบบไดนามิกสำหรับแถวอื่น ๆ
Ouroborus

1
ฉันคิดว่าparent_id INT NULLส่วนนั้น (โดยละเอียด) เท่ากับparent_id int default null

1
หมายเหตุด้านข้างสำหรับผู้ใช้ java หากคุณใช้ ibatis หรือ ORM อื่น ๆ และใช้แบบดั้งเดิมintแทนIntegerสมาชิกคลาสของคุณค่าเริ่มต้นจะไม่เป็นโมฆะ แต่จะเป็น 0 และคุณจะล้มเหลวข้อ จำกัด
Jim Ford

33

ฉันพบว่าเมื่อทำการแทรกค่าของคอลัมน์ null จะต้องถูกประกาศโดยเฉพาะว่าเป็น NULL มิฉะนั้นฉันจะได้รับข้อผิดพลาดในการละเมิดข้อ จำกัด (เมื่อเทียบกับสตริงว่าง)


8
คุณไม่สามารถตั้งค่าเริ่มต้นเป็น NULL บนคอลัมน์เพื่ออนุญาตได้หรือไม่?
Kevin Coulombe

ใช่ในภาษาส่วนใหญ่ NULL แตกต่างจากสตริงว่างเปล่า อาจเป็นเรื่องละเอียดอ่อนเมื่อเริ่มต้น แต่สำคัญที่ต้องจำ
Gary

สวัสดี Backslider คุณพูดว่า "(ตรงข้ามกับสตริงว่างเปล่า)" แต่ฉันไม่คิดว่าคุณหมายความว่าคุณจะแทรกค่าของสตริงว่าง แต่คุณไม่ได้ระบุค่าสำหรับค่าที่ ทั้งหมด? เช่นคุณไม่ได้พูดถึงคอลัมน์ในของคุณINSERT INTO {table} {list_of_columns}? เพราะนั่นคือความจริงสำหรับฉัน การละเว้นการพูดถึงคอลัมน์ทำให้เกิดข้อผิดพลาด แต่การรวมและการตั้งค่าเป็น NULL อย่างชัดเจนจะแก้ไขข้อผิดพลาด ถ้าฉันถูกต้องฉันคิดว่าความคิดเห็นของ @ Gary ใช้ไม่ได้ (เพราะคุณไม่ได้หมายถึงสตริงว่าง) แต่ @Kevin Coulombe อาจเป็นประโยชน์ ...
The Red Pea

ใช่คำแนะนำของ @ KevinCoulombe ได้ผลฉันอธิบายวิธีการบรรลุเป้าหมายนี้ด้วยสคริปต์การย้ายข้อมูลของ Entity Framework Core ที่นี่
The Red Pea

สิ่งสำคัญเพื่อชี้ให้เห็นว่าเหตุผลที่ชัดเจนเมื่ออัพเดตเร็กคอร์ดที่มีคีย์ต่างประเทศ NULL ใช้กับประเภทสตริงเท่านั้น (varchar ฯลฯ ) เพราะมิฉะนั้นสตริงว่างจะถูกส่งเป็นค่าเริ่มต้น นี่เป็นกรณีของ MySQL และส่งผลให้เกิดข้อผิดพลาดด้านความสมบูรณ์ในการอัปเดต
CodeMantle

4

ใช่ว่าจะได้ผลตามที่คุณคาดหวัง แต่น่าเสียดายที่ฉันดูเหมือนจะมีปัญหาในการที่จะหาคำสั่งที่ชัดเจนในการนี้ในคู่มือ MySQL

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


โดยการออกแบบ Foreign Key จะต้องอ้างถึงคีย์บางตัว (Primary) ซึ่งไม่ใช่ NULL แต่ในช่วงการพัฒนาเมื่อเราจำเป็นต้องแทรกข้อมูลหลายรายการในตารางลูกก่อนซึ่งเราไม่รู้ว่าจะอ้างถึงใคร (ตารางหลัก) . นั่นคือเหตุผลที่เรามีค่า NULL ที่อนุญาต ในการผลิตที่มี NULL จะเป็นขั้นตอนการออกแบบที่สามารถพูดได้คร่าวๆ
vimal krishna

2

ข้างต้นใช้งานได้ แต่ไม่ได้ผล หมายเหตุ ON DELETE CASCADE

CREATE DATABASE t;
USE t;

CREATE TABLE parent (id INT NOT NULL,
                 PRIMARY KEY (id)
) ENGINE=INNODB;

CREATE TABLE child (id INT NULL, 
                parent_id INT NULL,
                FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE

) ENGINE=INNODB;


INSERT INTO child (id, parent_id) VALUES (1, NULL);
-- Query OK, 1 row affected (0.01 sec)

4
คุณหมายถึงอะไร 'ข้างต้น'? โปรดทราบว่าหากคุณกำลังอ้างถึงคำตอบอื่นคำสั่งซื้ออาจมีการเปลี่ยนแปลง
d219

2

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

หากข้อมูลที่ส่งถูกแคสต์หรือตีความเป็นสตริงว่างข้อมูลจะล้มเหลว อย่างไรก็ตามการตั้งค่าเป็น NULL อย่างชัดเจนเมื่อใส่หรืออัปเดตคุณก็พร้อมที่จะไป

แต่นี่เป็นความสนุกของการเขียนโปรแกรมไม่ใช่เหรอ? สร้างปัญหาของเราเองแล้วแก้ไข! ไชโย!


1

ฉันยังติดอยู่ในปัญหานี้ แต่ฉันแก้ไขได้ง่ายๆโดยการกำหนดคีย์ต่างประเทศเป็นunsigned integer. ค้นหาตัวอย่างด้านล่าง -

CREATE TABLE parent (
   id int(10) UNSIGNED NOT NULL,
    PRIMARY KEY (id)
) ENGINE=INNODB;

CREATE TABLE child (
    id int(10) UNSIGNED NOT NULL,
    parent_id int(10) UNSIGNED DEFAULT NULL,
    FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE
) ENGINE=INNODB;

1

อีกวิธีหนึ่งคือการแทรกองค์ประกอบ DEFAULT ในตารางอื่น ตัวอย่างเช่นการอ้างอิงถึง uuid = 00000000-0000-0000-0000-000000000000 บนตารางอื่น ๆ จะบ่งชี้ว่าไม่มีการดำเนินการใด ๆ คุณต้องตั้งค่าทั้งหมดสำหรับ id นั้นให้เป็น "เป็นกลาง" เช่น 0 สตริงว่างค่า null เพื่อไม่ให้ส่งผลกระทบต่อตรรกะโค้ดของคุณ


2
นี่ไม่ใช่สิ่งเดียวกัน ค่าเริ่มต้นหรือค่า "เป็นกลาง" ไม่เหมือนกับค่า NULL ซึ่งไม่มีค่า โดยไม่ต้องพูดถึงข้อดีของค่าเริ่มต้นในค่า NULL วลีของคุณจะผสมกันเล็กน้อย "อีกวิธีหนึ่งคือการแทรกองค์ประกอบว่างในตารางอื่น" ควรพูดในบางสิ่งเช่น "อีกวิธีหนึ่งคือการแทรกองค์ประกอบเริ่มต้นในตารางอื่น"
blindguy
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.