ข้อ จำกัด กุญแจต่างประเทศ: เมื่อใดที่จะใช้ ON UPDATE และ ON DELETE


196

ฉันออกแบบสคีมาฐานข้อมูลของฉันโดยใช้ MySQL Workbench ซึ่งค่อนข้างเจ๋งเพราะคุณสามารถทำไดอะแกรมและแปลงมัน: P

อย่างไรก็ตามฉันตัดสินใจใช้ InnoDB เพราะมันรองรับ Foreign Key สิ่งหนึ่งที่ฉันสังเกตเห็นคือมันช่วยให้คุณสามารถตั้งค่าในการปรับปรุงและตัวเลือกการลบสำหรับคีย์ต่างประเทศ บางคนสามารถอธิบายว่า "จำกัด ", "เรียงซ้อน" และตั้งค่าเป็นโมฆะได้อย่างไรในตัวอย่างง่ายๆ

ตัวอย่างเช่นสมมติว่าผมมีตารางซึ่งรวมถึงuser userIDและบอกว่าฉันมีตารางข้อความmessageซึ่งมีหลายต่อหลายคนที่มีคีย์ต่างประเทศ 2 อัน (ซึ่งอ้างอิงคีย์หลักเดียวกันuserIDในuserตาราง) การตั้งค่าตัวเลือก On Update และ On Delete มีประโยชน์ในกรณีนี้หรือไม่? ถ้าเป็นเช่นนั้นฉันจะเลือกอันไหน หากนี่ไม่ใช่ตัวอย่างที่ดีคุณช่วยยกตัวอย่างที่ดีเพื่ออธิบายว่าสิ่งเหล่านี้มีประโยชน์หรือไม่

ขอบคุณ

คำตอบ:


485

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

ด้วย MySQL คุณไม่ได้มีข้อ จำกัด ขั้นสูงเหมือนอย่างที่คุณมีใน postgreSQL แต่อย่างน้อยก็มีข้อ จำกัด ของ foreign key ค่อนข้างสูง

เราจะยกตัวอย่างตาราง บริษัท ที่มีตารางผู้ใช้ที่มีบุคคลจาก บริษัท เหล่านี้

CREATE TABLE COMPANY (
     company_id INT NOT NULL,
     company_name VARCHAR(50),
     PRIMARY KEY (company_id)
) ENGINE=INNODB;

CREATE TABLE USER (
     user_id INT, 
     user_name VARCHAR(50), 
     company_id INT,
     INDEX company_id_idx (company_id),
     FOREIGN KEY (company_id) REFERENCES COMPANY (company_id) ON...
) ENGINE=INNODB;

ลองดูข้อON UPDATE :

  • ON UPDATE จํากัด : เริ่มต้น : ถ้าคุณพยายามที่จะปรับปรุง company_id ใน บริษัท ตารางเครื่องยนต์จะปฏิเสธการดำเนินการอย่างใดอย่างหนึ่งของผู้ใช้ในการเชื่อมโยงอย่างน้อยใน บริษัท นี้
  • เมื่อ UPDATE ไม่ดำเนินการ : เช่นเดียวกับข้อ จำกัด
  • ON CASCADE UPDATE : ดีที่สุดโดยปกติ : หากคุณอัปเดต company_id ในแถวของตาราง บริษัท เครื่องยนต์จะอัปเดตตามลำดับในแถว USER ทั้งหมดที่อ้างอิง บริษัท นี้ (แต่ไม่มีทริกเกอร์เปิดใช้งานบนตาราง USER คำเตือน) เครื่องยนต์จะติดตามการเปลี่ยนแปลงสำหรับคุณมันดี
  • ON UPDATE SET NULL : หากคุณอัปเดต company_id ในแถวของตาราง บริษัท เครื่องยนต์จะตั้ง USER ที่เกี่ยวข้อง company_id เป็น NULL (ควรมีอยู่ในฟิลด์ USER company_id) ฉันไม่เห็นสิ่งที่น่าสนใจเกี่ยวกับสิ่งนั้นในการอัปเดต แต่ฉันอาจผิด

และในตอนนี้ทางด้านลบ :

  • ON DELETE RESTRICT : ค่าเริ่มต้น : หากคุณพยายามลบรหัส company_id ในตาราง บริษัท เครื่องยนต์จะปฏิเสธการดำเนินการหากผู้ใช้อย่างน้อยหนึ่งลิงก์ของ บริษัท นี้สามารถช่วยชีวิตคุณได้
  • เมื่อลบไม่มีการดำเนินการ : เหมือนกับ RESTRICT
  • ON DELETE CASCADE : อันตราย : ถ้าคุณลบแถว บริษัท ในตาราง บริษัท เครื่องยนต์จะลบ USER ที่เกี่ยวข้องเช่นกัน สิ่งนี้เป็นอันตราย แต่สามารถใช้เพื่อทำการล้างข้อมูลอัตโนมัติบนตารางรอง (ดังนั้นจึงอาจเป็นสิ่งที่คุณต้องการ แต่ไม่แน่นอนสำหรับ บริษัท <-> ตัวอย่างผู้ใช้)
  • ON DELETE SET NULL : หยิบ : ถ้าคุณลบแถว บริษัท ผู้ใช้ที่เกี่ยวข้องจะมีความสัมพันธ์กับ NULL โดยอัตโนมัติ หาก Null เป็นค่าของคุณสำหรับผู้ใช้ที่ไม่มี บริษัท สิ่งนี้อาจเป็นพฤติกรรมที่ดีตัวอย่างเช่นคุณอาจต้องการให้ผู้ใช้อยู่ในแอปพลิเคชันของคุณในฐานะผู้เขียนเนื้อหาบางส่วน แต่การลบ บริษัท นั้นไม่ใช่ปัญหาสำหรับคุณ

มักจะเริ่มต้นของฉันคือ: ON ลบจํากัด ON UPDATE CASCADE ด้วยบางอย่างON DELETE CASCADEสำหรับตารางติดตาม (บันทึก - ไม่ใช่บันทึกทั้งหมด - สิ่งเช่นนั้น) และON DELETE SET NULLเมื่อตารางต้นแบบเป็น 'แอตทริบิวต์แบบง่าย' สำหรับตารางที่มีคีย์ต่างประเทศเช่นตาราง JOB สำหรับตาราง USER

แก้ไข

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

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

==> ดูการแก้ไขล่าสุดด้านล่างสิ่งต่าง ๆ กำลังเคลื่อนไหวในโดเมนนี้

ทริกเกอร์ไม่ได้เปิดใช้งานโดยการกระทำของ foreign key

และฉันไม่คิดว่าสิ่งนี้จะได้รับการแก้ไขในหนึ่งวัน ข้อ จำกัด กุญแจต่างประเทศได้รับการจัดการโดยหน่วยเก็บข้อมูล InnoDb และทริกเกอร์ได้รับการจัดการโดยโปรแกรม MySQL SQL ทั้งสองแยกจากกัน Innodb เป็นที่เก็บข้อมูลเดียวที่มีการจัดการข้อ จำกัด บางทีพวกเขาอาจจะเพิ่มทริกเกอร์ในเครื่องมือจัดเก็บข้อมูลได้โดยตรงในวันหนึ่งอาจจะไม่ใช่

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

12 / 2560- อัปเดตการแก้ไขนี้เกี่ยวกับ MySQL:

ตามที่ระบุโดย @IstiaqueAhmed ในความคิดเห็นสถานการณ์มีการเปลี่ยนแปลงในเรื่องนี้ ดังนั้นติดตามลิงค์และตรวจสอบสถานการณ์ล่าสุด (ซึ่งอาจมีการเปลี่ยนแปลงอีกในอนาคต)


8
ON DELETE CASCADE : dangerous- ใช้เกลือเล็กน้อย
oneday เมื่อ

3
คุณจะต้องระมัดระวังในการลดหลั่นมันสามารถล็อคระบบของคุณได้ถ้าจำเป็นต้องเปลี่ยนเรคคอร์ดจำนวนมาก การลบ Cascde ควรตรวจสอบอย่างรอบคอบก่อนใช้งานบ่อยครั้งที่คุณต้องการลบไม่เกิดขึ้นหากมีระเบียนย่อย ฉันไม่ต้องการให้ลูกค้าลบเพื่อล้างข้อมูลทางการเงินสำหรับ oreders ที่เขามีก่อนหน้านี้ บางครั้งมันจะดีกว่าเพื่อให้แน่ใจว่า cacading ไม่ได้อยู่และให้วิธีการ amrk บันทึกเป็นไม่ใช้งาน
HLGEM

1
ในแง่ของตรรกะทางธุรกิจมีกรณีหนึ่งที่น่าสนใจSET NULLในON UPDATE: การอัปเดต บริษัท หมายถึงการปลด บริษัท > ความสัมพันธ์กับผู้ใช้ เช่น: หาก บริษัท มีการเปลี่ยนแปลงประเภทธุรกิจผู้ใช้คนก่อนหน้านี้อาจไม่เกี่ยวข้องกับธุรกิจนั้นอีกต่อไปดังนั้นNULLอาจเหมาะสำหรับดัชนีนี้
CPHPython

1
@regilero ดูเหมือนว่าเนื้อหาในลิงก์แรกของคุณ ( dev.mysql.com/doc/refman/5.6/th/triggers.html ) ไปยังไซต์ mysql มีการเปลี่ยนแปลง มันบอกว่าThis includes changes to base tables that underlie updatable viewsแทนที่จะเป็นสิ่งที่คุณวางไว้They do not activate for changes in views
Istiaque Ahmed

6
"ฉันไม่ต้องการให้ลูกค้าลบเพื่อล้างข้อมูลทางการเงินสำหรับคำสั่งซื้อที่เขาเคยทำ" ในสถานการณ์เช่นนั้นคุณอาจยังต้องการข้อมูลลูกค้าอยู่ดี การออกแบบของคุณอาจทำเครื่องหมายว่าลูกค้าไม่ทำงานไม่ลบแถวของพวกเขาออกจากฐานข้อมูล ในทางปฏิบัติจะได้รับประสบการณ์ระดับมืออาชีพของฉันที่คุณจริงมากไม่ค่อยต้องการลบอะไรเลือกที่จะทำเครื่องหมายไม่ได้ใช้งานโดยค่าเริ่มต้น ในกรณีที่การลบอย่างถาวรก็โอเคCASCADE DELETEโดยทั่วไปก็ถือว่าใช้ได้เช่นกัน ฉันไม่คิดว่ามันอันตรายอย่างยิ่ง
GrandOpener

3

นอกจาก @MarkR คำตอบ - สิ่งหนึ่งที่ควรทราบก็คือกรอบ PHP จำนวนมากที่มี ORMs จะไม่รู้จักหรือใช้การตั้งค่าฐานข้อมูลขั้นสูง (กุญแจต่างประเทศ, การลบซ้อน, ข้อ จำกัด ที่ไม่ซ้ำกัน) และสิ่งนี้อาจทำให้เกิดพฤติกรรมที่ไม่คาดคิด

ตัวอย่างเช่นถ้าคุณลบระเบียนโดยใช้ ORM และคุณDELETE CASCADEจะลบระเบียนในตารางที่เกี่ยวข้องความพยายามของ ORM ในการลบระเบียนที่เกี่ยวข้องเหล่านี้ (มักจะเป็นอัตโนมัติ) จะส่งผลให้เกิดข้อผิดพลาด


11
นั่นจะเป็นเหตุผลที่จะไม่ใช้ ORM นั้น เครื่องมือที่สนับสนุนฐานข้อมูลไม่ดีไม่น่าเชื่อถือ foreign key และ cascading deletes หรือการอัพเดทเป็นพื้นฐานของ db ไม่ใช่แนวคิดขั้นสูงและไม่ควรออกแบบฐานข้อมูล realtional โดยไม่มีข้อ จำกัด ของ foreign key!
HLGEM

ปัญหาคือพวกเขาโยนข้อผิดพลาด เป็นไปได้หรือไม่ที่จะ จำกัด การลบ แต่มีเอ็นจิ้นไม่สร้างข้อผิดพลาด แต่ยังคงรักษาความหมายไว้ ฉันต้องการให้โปรแกรมของฉันยังคงดำเนินต่อไปในขณะเดียวกันก็ป้องกันข้อมูลอื่นไม่ให้ถูกลบ
TheRealChx101

2

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

พิจารณาว่าใบสมัครของคุณควรตอบสนองต่อกรณีต่างๆอย่างไร

การกระทำเริ่มต้นคือการ จำกัด (เช่นไม่อนุญาต) การดำเนินการซึ่งโดยปกติจะเป็นสิ่งที่คุณต้องการในขณะที่มันป้องกันข้อผิดพลาดการเขียนโปรแกรมโง่ อย่างไรก็ตามใน DELETE CASCADE ก็มีประโยชน์เช่นกัน ขึ้นอยู่กับแอปพลิเคชันของคุณและวิธีที่คุณต้องการลบวัตถุบางอย่าง

โดยส่วนตัวแล้วฉันจะใช้ InnoDB เพราะมันไม่ได้ทำให้ข้อมูลของคุณ (cf MyISAM ซึ่งทำได้) แทนที่จะเป็นเพราะมันมีข้อ จำกัด FK

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