【问题标题】:Tables that 'extend' other tables“扩展”其他表的表
【发布时间】:2014-07-04 13:04:54
【问题描述】:

我有一个名为dbo.Vendor 的表存储有关供应商的信息。它有这样的字段:

 1. VendorID
 2. VendorName
 3. VendorType
 4. AddressLine1
 5. EMail
 6. Telephone
 7. and so on....

此信息对所有供应商都是通用的。但根据供应商的类型(VendorType 字段),我需要收集更具体的信息。例如,作为慈善机构的供应商将拥有一个慈善编号,但作为律师的供应商将拥有某种合法的注册号。如果供应商是电影院,那么我可能需要知道座位容量,这当然不适用于其他供应商。

我真的必须为每个不同的供应商创建一个唯一的表吗? dbo.VendorLawdbo.VendorCinema。或者我可以在dbo.Vendor 主表中创建所有可能的字段,并在该字段不适用于该供应商的地方留下NULL 值吗?这当然违反了规范化规则。

【问题讨论】:

    标签: sql-server database-design


    【解决方案1】:

    根据每种供应商类型需要多少附加可选信息的范围,我将创建另外两个表:一个存储所有不同类型的附加信息的参考表,以及一个存储所有记录的表(和链接到主表)。

    CREATE TABLE schema.VendorAdditionalInfo (
       autoId serial NOT NULL,
       vendorId int,
       vendorInfoId int,
       vendorInfoText varchar
    );
    

    然后创建您的参考表:

    CREATE TABLE schema.VendorInfo (
       vendorInfoId serial NOT NULL,
       vendorType int,
       vendorInfoName text
    )
    

    通过这种方式,您可以根据供应商类型在 VendorAdditionalInfo 中创建任意数量的记录。

    编辑:您输入的信息示例:

    INSERT INTO schema.VendorInfo (vendorType, vendorInfoName)
    VALUES 
    (1, 'Lawyer Registration Number'),
    (2, 'Nurse ID Number'),
    (3, 'Hot Dog Business License')
    

    然后对于您的记录表,您将输入您的信息:

    INSERT INTO schema.VendorAdditionalInfo (vendorId, vendorInfoId, vendorInfoText)
    VALUES
    (10, 1, 'LAW13245'),
    (11, 2, 'NURSE234234'),
    (12, 1, 'LAW56156'),
    (13, 3, 'HOTDOGBUSINESSLIC23')
    

    基本上 - 文本字段是每个附加信息类型唯一的字段。

    【讨论】:

    • 哇,这实际上非常聪明。请问第一个表中的“vendorInfoText”字段是什么?
    • 这将包括您需要的附加信息的值。我已经更新了我的答案,使其更加清晰并显示插入示例。
    • 我看到的唯一问题是 vendorInfoText 将包含所有相同数据类型的值。我无法为每个额外字段指定不同的数据类型。鉴于供应商的数量可能会增长到数十万,这是否会导致可扩展性问题?
    • 这是一个限制;你能提供除注册号以外的例子吗?
    • 这称为实体属性值 (EAV) 模型。这是众所周知的,并且有很多关于它是否是一个好主意的讨论。它归结为可维护性 (EAV) 和性能(一行上有无数列)之间的平衡行为。无论哪种方式,您的应用程序代码都需要了解特定供应商类型的特定字段。 en.wikipedia.org/wiki/…
    【解决方案2】:

    我会创建额外的表格。这允许您根据供应商类型轻松实施空/非空(和其他)约束 - 您甚至可以在 (VendorID,VendorType) 上的现有表中创建一个超级键,并在每个供应商特定列中创建一个计算列以确保例如只有影院供应商在 VendorCinema 表中有条目。

    CREATE TABLE Vendors (
       VendorID int IDENTITY(-47,1) not null,
       VendorName varchar(19) not null,
       VendorType varchar(11) not null,
       AddressLine1 varchar(35) not null,
       EMail varchar(312) null,
       Telephone varchar(15) null,
       constraint PK_Vendors PRIMARY KEY (VendorID),
       constraint UQ_Vendor_Types UNIQUE (VendorID,VendorType),
       constraint CK_Vendor_Types CHECK (VendorType in ('Law','Cinema'))
    

    )

    CREATE TABLE CinemaVendors (
        VendorID int not null,
        VendorType as CONVERT(varchar(11),'Cinema') persisted,
        Seating int not null,
        BruceWillisMovies int not null,
        constraint PK_CinemaVendors PRIMARY KEY (VendorID),
        constraint FK_CinemaVendors_Vendors FOREIGN KEY
                                    (VendorID,VendorType)
                 references Vendors (VendorID,VendorType),
        constraint CK_BruceWillisMovies CHECK (BruceWillisMovies > 3)
    )
    

    这比在一个表中拥有大量可为空的列然后尝试强制执行所有实际约束要容易得多。

    这也解决了 EAV 模型的问题 - 我们希望为影院供应商存储一个 int,我们确定实际上已经存储了一个 int

    (您是否还根据VendorID 列在上述两个表之间声明外键是可选的。有时我会,有时我不会。这是“真正的”外键,但我们使用这两个上面的第一列,以确保只有Cinema 供应商最终出现在CinemaVendors 表中)

    【讨论】:

    • 这行得通,但是现在我如何编写 SQL 查询以从 Vendors 表中选择所有内容,然后仅当 VendorType 是“电影院”时才从 CinemaVendors 中选择所有内容?我是否必须编写各种 IF ELSE 语句来涵盖所有可能的不同类型的供应商?
    • 只是补充一下,我只能传入 VendorID 作为网站的搜索条件。
    • 您通过 XVendors 加入供应商以获取所有 XVendor 信息。在特定 X 的上下文中,您使用它的表。当您在处理所有供应商时执行特定于 X 的操作时,您只需要在供应商 VendorType 上进行分支/案例。
    • 那么在所有 XVendor 表上一直 LEFT JOIN?
    • LEFT JOIN 为不匹配的值添加空值。您是否想要一堆包含非 XVendor 供应商 ID 和空值的行?
    猜你喜欢
    • 1970-01-01
    • 2014-08-22
    • 2017-06-15
    • 2020-08-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多