ฉันจะแมปความสัมพันธ์ IS-A กับฐานข้อมูลได้อย่างไร


26

พิจารณาสิ่งต่อไปนี้:

entity User
{
    autoincrement uid;
    string(20) name;
    int privilegeLevel;
}

entity DirectLoginUser
{
    inherits User;
    string(20) username;
    string(16) passwordHash;
}

entity OpenIdUser
{
    inherits User;
    //Whatever attributes OpenID needs... I don't know; this is hypothetical
}

ผู้ใช้ประเภทต่าง ๆ (ผู้ใช้ที่ล็อกอินโดยตรงและผู้ใช้ OpenID) แสดงความสัมพันธ์ IS-A กล่าวคือผู้ใช้ทั้งสองประเภทเป็นผู้ใช้ ตอนนี้มีหลายวิธีที่สิ่งนี้สามารถแสดงใน RDBMS:

วิธีที่หนึ่ง

CREATE TABLE Users
(
    uid INTEGER AUTO_INCREMENT NOT NULL,
    name VARCHAR(20) NOT NULL,
    privlegeLevel INTEGER NOT NULL,
    type ENUM("DirectLogin", "OpenID") NOT NULL,
    username VARCHAR(20) NULL,
    passwordHash VARCHAR(20) NULL,
    //OpenID Attributes
    PRIMARY_KEY(uid)
)

วิธีที่สอง

CREATE TABLE Users
(
    uid INTEGER AUTO_INCREMENT NOT NULL,
    name VARCHAR(20) NOT NULL,
    privilegeLevel INTEGER NOT NULL,
    type ENUM("DirectLogin", "OpenID") NOT NULL,
    PRIMARY_KEY(uid)
)

CREATE TABLE DirectLogins
(
    uid INTEGER NOT_NULL,
    username VARCHAR(20) NOT NULL,
    passwordHash VARCHAR(20) NOT NULL,
    PRIMARY_KEY(uid),
    FORIGEN_KEY (uid) REFERENCES Users.uid
)

CREATE TABLE OpenIDLogins
(
    uid INTEGER NOT_NULL,
    // ...
    PRIMARY_KEY(uid),
    FORIGEN_KEY (uid) REFERENCES Users.uid
)

วิธีที่สาม

CREATE TABLE DirectLoginUsers
(
    uid INTEGER AUTO_INCREMENT NOT NULL,
    name VARCHAR(20) NOT NULL,
    privlegeLevel INTEGER NOT NULL,
    username VARCHAR(20) NOT NULL,
    passwordHash VARCHAR(20) NOT NULL,
    PRIMARY_KEY(uid)
)

CREATE TABLE OpenIDUsers
(
    uid INTEGER AUTO_INCREMENT NOT NULL,
    name VARCHAR(20) NOT NULL,
    privlegeLevel INTEGER NOT NULL,
    //OpenID Attributes
    PRIMARY_KEY(uid)
)

ฉันเกือบแน่ใจว่าวิธีที่สามเป็นวิธีที่ผิดเพราะไม่สามารถทำการเชื่อมต่อกับผู้ใช้ที่อื่นในฐานข้อมูลได้

ตัวอย่างโลกแห่งความจริงของฉันไม่ใช่ผู้ใช้ที่มีตัวอย่างการเข้าสู่ระบบที่แตกต่างกัน ฉันสนใจวิธีการสร้างแบบจำลองความสัมพันธ์นี้ในกรณีทั่วไป


ฉันได้แก้ไขคำตอบของฉันเพื่อรวมวิธีการที่แนะนำในความคิดเห็นโดย Joel Brown มันควรจะทำงานให้คุณ หากคุณกำลังมองหาคำแนะนำเพิ่มเติมฉันจะติดแท็กคำถามของคุณอีกครั้งเพื่อระบุว่าคุณกำลังมองหาคำตอบเฉพาะ MySQL
Nick Chammas

โปรดทราบว่ามันขึ้นอยู่กับวิธีการใช้ความสัมพันธ์ในฐานข้อมูลที่เหลือ ความสัมพันธ์ที่เกี่ยวข้องกับเอนทิตีลูกต้องการคีย์ต่างประเทศเพียงแค่ตารางเหล่านั้นและห้ามแมปไปยังตารางที่รวมเป็นหนึ่งเดียวโดยไม่มีการแฮ็ก
beldaz

ในฐานข้อมูลเชิงวัตถุเช่น PostgreSQL นั้นมีวิธีที่สี่: คุณสามารถประกาศ INHERITS ของตารางจากตารางอื่น postgresql.org/docs/current/static/tutorial-inheritance.html
MarkusSchaber

คำตอบ:


16

วิธีที่สองเป็นวิธีที่ถูกต้อง

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

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

ตัวอย่างเช่น MySQL DDL จะมีลักษณะดังนี้:

CREATE TABLE Users
(
      uid               INTEGER AUTO_INCREMENT NOT NULL
    , type              ENUM("DirectLogin", "OpenID") NOT NULL
    // ...

    , PRIMARY_KEY(uid)
);

CREATE TABLE DirectLogins
(
      uid               INTEGER NOT_NULL
    , type              ENUM("DirectLogin") NOT NULL
    // ...

    , PRIMARY_KEY(uid)
    , FORIGEN_KEY (uid, type) REFERENCES Users (uid, type)
);

CREATE TABLE OpenIDLogins
(
      uid               INTEGER NOT_NULL
    , type              ENUM("OpenID") NOT NULL
    // ...

    PRIMARY_KEY(uid),
    FORIGEN_KEY (uid, type) REFERENCES Users (uid, type)
);

(บนแพลตฟอร์มอื่นคุณต้องใช้CHECKข้อ จำกัด แทนENUM) MySQL รองรับคีย์ต่างประเทศแบบคอมโพสิตดังนั้นจึงควรใช้งานได้

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

วิธีที่สามบังคับให้แบบสอบถามใด ๆ ที่อ้างอิงผู้ใช้ตรวจสอบกับทั้งสองตาราง สิ่งนี้ยังป้องกันคุณจากการอ้างอิงตารางผู้ใช้คนเดียวผ่านทางต่างประเทศ


1
ฉันจะจัดการกับความจริงที่ว่าการใช้วิธีที่ 2 ไม่มีวิธีใดที่จะบังคับใช้ว่ามีแถวที่เกี่ยวข้องหนึ่งตารางในอีกสองตาราง
Billy ONeal

2
@Billy - คัดค้านที่ดี หากผู้ใช้ของคุณมีอย่างใดอย่างหนึ่งเท่านั้นคุณสามารถบังคับใช้สิ่งนี้ผ่านเลเยอร์ proc หรือทริกเกอร์ ฉันสงสัยว่ามีวิธีระดับ DDL ในการบังคับใช้ข้อ จำกัด นี้หรือไม่ (อนิจจามุมมองที่มีการจัดทำดัชนีไม่อนุญาต UNIONหรือฉันอยากจะแนะนำมุมมองที่จัดทำดัชนีพร้อมกับดัชนีที่ไม่ซ้ำกับUNION ALLของuidจากสองตาราง)
Nick Chammas

แน่นอนว่า RDBMS ของคุณรองรับมุมมองที่จัดทำดัชนีไว้ตั้งแต่แรก
Billy ONeal

1
วิธีที่สะดวกในการใช้ข้อ จำกัด ข้ามตารางชนิดนี้คือการรวมแอตทริบิวต์การแบ่งพาร์ติชันไว้ในตารางชนิดซุปเปอร์ จากนั้นแต่ละประเภทย่อยสามารถตรวจสอบเพื่อให้แน่ใจว่าเกี่ยวข้องกับ super-types ที่มีค่าแอ็ตทริบิวต์การแบ่งพาร์ติชันที่เหมาะสมเท่านั้น วิธีนี้ช่วยประหยัดไม่ต้องทำการทดสอบการชนโดยดูที่ตารางย่อยประเภทอื่น
Joel Brown

1
@Joel - ตัวอย่างเช่นเราเพิ่มtypeคอลัมน์ลงในตารางย่อยแต่ละประเภทที่ถูก จำกัด ผ่านCHECKข้อ จำกัด เพื่อให้มีค่าเดียว (ประเภทของตารางนั้น) จากนั้นเราจะทำให้ปุ่มต่างประเทศย่อยตารางซุปเปอร์โต๊ะเป็นคนที่คอมโพสิตทั้งบนและuid typeนั่นเป็นความคิดสร้างสรรค์
Nick Chammas

5

พวกเขาจะถูกตั้งชื่อ

  1. มรดกตารางเดียว
  2. การสืบทอดคลาสของตาราง
  3. คอนกรีตมรดกตาราง

และทุกคนมีการใช้ที่ถูกต้องตามกฎหมายและได้รับการสนับสนุนจากห้องสมุด คุณต้องหาว่าแบบไหนดีที่สุด

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


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