จะมีความสัมพันธ์แบบตัวต่อตัวกับเด็กที่มีสิทธิพิเศษอย่างไร


22

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

TableA
    Id            INT PRIMARY KEY

TableB
    Id            INT PRIMARY KEY
    Parent        INT NOT NULL FOREIGN KEY REFERENCES TableA.Id

วิธีที่ฉันเห็นฉันสามารถเพิ่มคอลัมน์ต่อไปนี้ใน TableA:

    FavoriteChild INT NULL FOREIGN KEY REFERENCES TableB.Id

หรือคอลัมน์ต่อไปนี้เพื่อ TableB:

    IsFavorite    BIT NOT NULL

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

เกณฑ์ประเภทใดที่ฉันควรใช้เพื่อกำหนดวิธีการที่จะใช้ หรือมีวิธีอื่นที่ฉันไม่ได้พิจารณาหรือไม่?

ฉันใช้ SQL Server 2012

คำตอบ:


19

อีกวิธีหนึ่ง (ไม่มี Nulls และไม่มีรอบในFOREIGN KEYความสัมพันธ์) คือการมีตารางที่สามเพื่อจัดเก็บ "เด็กที่ชื่นชอบ" ใน DBMS ส่วนใหญ่คุณจะต้องเพิ่มUNIQUEข้อ จำกัด TableBใน

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

Parent
    ParentID        INT NOT NULL PRIMARY KEY

Child
    ChildID         INT NOT NULL PRIMARY KEY
    ParentID        INT NOT NULL FOREIGN KEY REFERENCES Parent (ParentID)
    UNIQUE (ParentID, ChildID)

FavoriteChild
    ParentID        INT NOT NULL PRIMARY KEY
    ChildID         INT NOT NULL 
    FOREIGN KEY (ParentID, ChildID) 
        REFERENCES Child (ParentID, ChildID)

ใน SQL-Server (ที่คุณกำลังใช้) คุณยังมีตัวเลือกของIsFavoriteคอลัมน์บิตที่คุณพูดถึง เด็กคนโปรดที่ไม่ซ้ำกันต่อผู้ปกครองสามารถทำได้ผ่านดัชนีที่ไม่ซ้ำกรอง

Parent
    ParentID        INT NOT NULL PRIMARY KEY

Child
    ChildID         INT NOT NULL PRIMARY KEY
    ParentID        INT NOT NULL FOREIGN KEY REFERENCES Parent (ParentID)
    IsFavorite      BIT NOT NULL

CREATE UNIQUE INDEX is_FavoriteChild
  ON Child (ParentID)
  WHERE IsFavorite = 1 ;

และเหตุผลหลักที่ไม่แนะนำให้ใช้ตัวเลือกที่ 1 ของคุณอย่างน้อยไม่ใช่ใน SQL-Server นั่นคือรูปแบบของเส้นทางวงกลมในการอ้างอิงคีย์ต่างประเทศมีปัญหาบางอย่าง

อ่านบทความที่ค่อนข้างเก่า: SQL โดยการออกแบบ: การอ้างอิงแบบวงกลม

เมื่อแทรกหรือลบแถวออกจากตารางทั้งสองคุณจะพบกับปัญหา "ไก่และไข่" ฉันควรแทรกตารางใดก่อน - โดยไม่ละเมิดข้อ จำกัด ใด ๆ

เพื่อแก้ปัญหานั้นคุณต้องกำหนดอย่างน้อยหนึ่งคอลัมน์ที่ไม่สามารถใช้ได้ (ตกลงทางเทคนิคคุณจะได้ไม่ต้องคุณสามารถมีคอลัมน์ทั้งหมดเป็นNOT NULLแต่เฉพาะใน DBMS เช่น Postgres และ Oracle ที่ได้ดำเนินการ จำกัด deferrable ดู @ คำตอบเออร์วินในคำถามที่คล้ายกัน:. คอมเพล็กซ์ จำกัด ที่สำคัญต่างประเทศใน SQLAlchemyเกี่ยวกับวิธีการ สิ่งนี้สามารถทำได้ใน Postgres) ยังตั้งค่านี้รู้สึกเหมือนเล่นสเก็ตบนน้ำแข็งบาง ๆ

ลองดูคำถามที่เกือบจะเหมือนกันที่ SO (แต่สำหรับ MySQL) ใน SQL มันจะโอเคไหมที่จะอ้างถึงสองตาราง? คำตอบของฉันก็ค่อนข้างเหมือนกัน MySQL ไม่มีดัชนีบางส่วนดังนั้นทางเลือกเดียวที่ทำงานได้คือ FK แบบ nullable และโซลูชันตารางพิเศษ


9

ขึ้นอยู่กับความสำคัญของคุณ คุณต้องการที่จะหลีกเลี่ยงการทำงานหรือคุณต้องการที่จะปฏิบัติตามกฎที่เข้มงวดที่สุดของการฟื้นฟู?

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

คำแนะนำของ @ypercube ก็เป็นสิ่งที่ดีเช่นกัน

โปรดอย่ากรุณาทิ้งสคีมาของคุณด้วยชื่อคอลัมน์ที่ไม่มีความหมายเช่นIdกัน ฉันค่อนข้างจะเห็นIdชื่อในวิธีที่มีความหมายตลอดทั้งสคี มันระบุผู้เขียนหรือไม่? AuthorIDตกลงเรียกมันว่า มันเป็นตัวแทนของผลิตภัณฑ์หรือไม่? ProductIDตกลง เป็นพนักงานหรือไม่และในบางกรณีมีการอ้างอิงผู้จัดการ ตกลงEmployeeIDและManagerIDทำให้รู้สึกมากขึ้นกว่าที่ฉันและID Parentแม้ว่ามันอาจดูสมเหตุสมผลที่จะละทิ้งสิ่งนั้น (และซ้ำซ้อนเพื่อใส่ไว้ใน) เมื่อคุณเริ่มเขียนการรวมที่ซับซ้อน (หรือโพสต์ข้อความค้นหาที่นี่) คุณจะรู้สึกถึงการสาปแช่งอย่างแน่นอนเมื่อคุณพยายามที่จะ ไปยังคอลัมน์เช่นa.Parent = b.ID... blecch


1

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

อย่างไรก็ตามความคิดของ @ypercube เกี่ยวกับตารางแยกเป็นสิ่งที่ดีเช่นกัน

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