【问题标题】:How to implement one-to-one, one-to-many and many-to-many relationships while designing tables?设计表格时如何实现一对一、一对多和多对多的关系?
【发布时间】:2011-11-09 22:38:42
【问题描述】:

谁能解释一下在设计表格时如何通过一些示例来实现一对一、一对多和多对多的关系?

【问题讨论】:

    标签: sql database-design foreign-keys relational-database relationship


    【解决方案1】:

    一对一:对引用的表使用外键:

    student: student_id, first_name, last_name, address_id
    address: address_id, address, city, zipcode, student_id # you can have a
                                                            # "link back" if you need
    

    您还必须对外键列 (addess.student_id) 设置唯一约束,以防止子表 (address) 中的多行与引用表 (student) 中的同一行相关联。

    一对多:在关系的多端使用外键链接回“一”端:

    teachers: teacher_id, first_name, last_name # the "one" side
    classes:  class_id, class_name, teacher_id  # the "many" side
    

    多对多:使用联结表 (example):

    student: student_id, first_name, last_name
    classes: class_id, name, teacher_id
    student_classes: class_id, student_id     # the junction table
    

    查询示例:

     -- Getting all students for a class:
    
        SELECT s.student_id, last_name
          FROM student_classes sc 
    INNER JOIN students s ON s.student_id = sc.student_id
         WHERE sc.class_id = X
    
     -- Getting all classes for a student: 
    
        SELECT c.class_id, name
          FROM student_classes sc 
    INNER JOIN classes c ON c.class_id = sc.class_id
         WHERE sc.student_id = Y
    

    【讨论】:

    • 什么是“链接返回”在一对一关系中有用的一个很好的例子?感谢您简洁明了的回答。
    • @dev_feed 在数据库设计方面,我不认为返回链接是有益的,但使用返回链接上面的示例可以简化在给定address 的情况下查找student。跨度>
    • 每个student_classes 行将有2 个一对一 关系,对吧?一个student 行有很多班级,一个classes 行有很多学生——但一个student_classes 行每个人只有一个(?)。
    • @Cody 每个student_classes 行应该只有一个一对一的关系。如果studentAclassAclassB 中,那么student_classes 中应该有两行,一行代表什么关系。
    • 在一对一的关系中,连接字段在两个表中应该是唯一的。它可能是一个表上的 PK 保证唯一性,但它可能需要另一个表上的唯一索引。
    【解决方案2】:

    以下是关系类型的一些真实示例:

    一对一 (1:1)

    当且仅当表 A 中的一条记录与表 B 中的最多一条记录相关时,关系是一对一的。

    要建立一对一的关系,表B(无孤记录)的主键必须是表A(有孤记录)的辅助键。

    例如:

    CREATE TABLE Gov(
        GID number(6) PRIMARY KEY, 
        Name varchar2(25), 
        Address varchar2(30), 
        TermBegin date,
        TermEnd date
    ); 
    
    CREATE TABLE State(
        SID number(3) PRIMARY KEY,
        StateName varchar2(15),
        Population number(10),
        SGID Number(4) REFERENCES Gov(GID), 
        CONSTRAINT GOV_SDID UNIQUE (SGID)
    );
    
    INSERT INTO gov(GID, Name, Address, TermBegin) 
    values(110, 'Bob', '123 Any St', '1-Jan-2009');
    
    INSERT INTO STATE values(111, 'Virginia', 2000000, 110);
    

    一对多 (1:M)

    当且仅当表 A 中的一条记录是一对多的关系 与表 B 中的一条或多条记录相关。但是,表 B 中的一条记录不能与表 A 中的多条记录相关。

    要建立一对多关系,表 A(“one”表)的主键必须是表 B(“many”表)的辅助键。

    例如:

    CREATE TABLE Vendor(
        VendorNumber number(4) PRIMARY KEY,
        Name varchar2(20),
        Address varchar2(20),
        City varchar2(15),
        Street varchar2(2),
        ZipCode varchar2(10),
        Contact varchar2(16),
        PhoneNumber varchar2(12),
        Status varchar2(8),
        StampDate date
    );
    
    CREATE TABLE Inventory(
        Item varchar2(6) PRIMARY KEY,
        Description varchar2(30),
        CurrentQuantity number(4) NOT NULL,
        VendorNumber number(2) REFERENCES Vendor(VendorNumber),
        ReorderQuantity number(3) NOT NULL
    );
    

    多对多 (M:M)

    当且仅当表 A 中的一条记录与表 B 中的一条或多条记录相关时,关系是多对多的,反之亦然。

    要建立多对多关系,请创建名为“ClassStudentRelation”的第三个表,该表将具有表 A 和表 B 的主键。

    CREATE TABLE Class(
        ClassID varchar2(10) PRIMARY KEY, 
        Title varchar2(30),
        Instructor varchar2(30), 
        Day varchar2(15), 
        Time varchar2(10)
    );
    
    CREATE TABLE Student(
        StudentID varchar2(15) PRIMARY KEY, 
        Name varchar2(35),
        Major varchar2(35), 
        ClassYear varchar2(10), 
        Status varchar2(10)
    );  
    
    CREATE TABLE ClassStudentRelation(
        StudentID varchar2(15) NOT NULL,
        ClassID varchar2(14) NOT NULL,
        FOREIGN KEY (StudentID) REFERENCES Student(StudentID), 
        FOREIGN KEY (ClassID) REFERENCES Class(ClassID),
        UNIQUE (StudentID, ClassID)
    );
    

    【讨论】:

    • 第一个例子:GID 号(6)和 SGID 号(4),为什么? SGID 不应该也是 (6) 吗?在第二个例子中 number(4) 和 number(2)...
    • @obeliksz 可能是空值?
    • 为什么要在 M:N 的末尾使用 UNIQUE (StudentID, ClassID)?
    • @strix25 强制避免在多次创建相同的 ClassStudentRelation 行时重复,因为如果您不能确保外键 StudentID 和 ClassID 都是唯一的,那么会停止创建具有相同 StudentID 的新行和 ClassID ?因为它们在上面的代码中不是唯一的。所以你要么像上面的代码那样实现它,要么添加一个包含 StudentID 和 ClassID 的主键,以避免在 ClassStudentRelation 中重复创建同一行。
    • @valik 数据库中的数据通过引用现有数据来工作,而不是多次创建相同的数据,您为什么要这样做?当然你不必这样做,否则效率不高。考虑到这一点,让我们回到你的例子(詹姆斯有生物学,生物学有詹姆斯),你当然可以,但是不需要创建数据库中已经存在的另一条数据。您需要做的就是在您想要创建任何关系时仅引用已经存在的关系。我希望这会有所帮助:)
    【解决方案3】:

    一对多

    一对多的表关系如下:

    在关系数据库系统中,一对多表关系基于子表中的 Foreign Key 列链接两个表,该列引用父表行的 Primary Key

    在上面的表格图中,post_comment 表中的post_id 列与post 表id Primary Key 列有Foreign Key 关系:

    ALTER TABLE
        post_comment
    ADD CONSTRAINT
        fk_post_comment_post_id
    FOREIGN KEY (post_id) REFERENCES post
    

    一对一

    一对一的表关系如下:

    在关系数据库系统中,一对一的表关系基于子表中的Primary Key 列链接两个表,该列也是引用父表行的Primary KeyForeign Key

    因此,我们可以说子表与父表共享Primary Key

    在上表图中,post_details 表中的id 列与postidPrimary Key 列也有Foreign Key 关系:

    ALTER TABLE
        post_details
    ADD CONSTRAINT
        fk_post_details_id
    FOREIGN KEY (id) REFERENCES post
    

    多对多

    多对多表关系如下:

    在关系数据库系统中,多对多表关系通过一个子表链接两个父表,该子表包含两个引用两个父表的Primary Key 列的Foreign Key 列。

    在上面的表格图中,post_tag 表中的post_id 列与post 表id Primary Key 列也有Foreign Key 关系:

    ALTER TABLE
        post_tag
    ADD CONSTRAINT
        fk_post_tag_post_id
    FOREIGN KEY (post_id) REFERENCES post
    

    并且,post_tag 表中的tag_id 列与tag 表ID Primary Key 列具有Foreign Key 关系:

    ALTER TABLE
        post_tag
    ADD CONSTRAINT
        fk_post_tag_tag_id
    FOREIGN KEY (tag_id) REFERENCES tag
    

    【讨论】:

    • 嘿。我有一个问题:在多对多关系中,是否也需要将外键设置为主键?
    • @vallim 在链接表中,PK 是两个 FK 的组合。
    【解决方案4】:

    一对一 (1-1) 关系: 这是主键和外键之间的关系(与外键相关的主键只有一条记录)。这是一对一的关系。

    一对多 (1-M) 关系: 这也是主键和外键之间的关系,但这里的主键与多条记录相关(即表 A 有书籍信息,表 B 有多个出版商的一本书)。

    多对多(M-M):多对多包括两个维度,下面用示例进行充分解释。

    -- This table will hold our phone calls.
    CREATE TABLE dbo.PhoneCalls
    (
       ID INT IDENTITY(1, 1) NOT NULL,
       CallTime DATETIME NOT NULL DEFAULT GETDATE(),
       CallerPhoneNumber CHAR(10) NOT NULL
    )
    -- This table will hold our "tickets" (or cases).
    CREATE TABLE dbo.Tickets
    (
       ID INT IDENTITY(1, 1) NOT NULL,
       CreatedTime DATETIME NOT NULL DEFAULT GETDATE(),
       Subject VARCHAR(250) NOT NULL,
       Notes VARCHAR(8000) NOT NULL,
       Completed BIT NOT NULL DEFAULT 0
    )
    -- This table will link a phone call with a ticket.
    CREATE TABLE dbo.PhoneCalls_Tickets
    (
       PhoneCallID INT NOT NULL,
       TicketID INT NOT NULL
    )
    

    【讨论】:

    • 如果你也添加了主键和外键约束会更好更清晰。
    猜你喜欢
    • 1970-01-01
    • 2013-05-27
    • 1970-01-01
    • 2015-02-06
    • 1970-01-01
    • 2011-08-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多