【问题标题】:Are guaranteed null fields an indication of poor database design?保证空字段是否表明数据库设计不佳?
【发布时间】:2014-08-28 23:37:55
【问题描述】:

我正在开发一个批处理应用程序,该应用程序允许用户提交有关特定车辆信息的请求。用户可以使用 VIN 或牌照/州组合提交请求。我提出了如下表结构:


待处理车辆

vehicle_id(fk)|user_id(fk)|status|start_time

车辆

vehicle_id|VIN|plate|state


我的同事认为这是糟糕的设计,因为 Vehicles 中的每条记录要么有一个空 VIN 字段,要么有一个空车牌和状态字段。相反,他们提出了以下建议:
待处理车辆

vehicle_id(fk)|user_id(fk)|status|start_time

车辆

vehicle_id(pk)|field|value

Vehicles 中的条目将包含一行用于 vin:

1|"vin"|"123

或两行代表一个板块/州:

2|"plate"|"abc 123" 2|"state"|"NY"


我认为第一个解决方案会更容易查询,而且没有任何明显的缺点。应该首选哪种设计?保证为空字段真的是糟糕设计的指标吗?

【问题讨论】:

  • 我不确定它应该如何与两行一起工作来表示板/状态。如果vehicle_id(pk) 是主键,则不能有两行具有相同的vehicle_id。另外,据我所知,每辆车都必须有 VIN(可能未知),但不能有车牌。此外,车牌号会随时间变化(可能会发生变化)。

标签: database relational-database database-schema


【解决方案1】:

你的同事提出的是关于数据库设计中的终极反模式。

Google 为 Bill Karwin 的“反模式”一书和“EAV”。

询问您的同事,他建议如何强制“plate”和“state”值始终成对出现在他的数据库中。如果他指向应用程序代码,请询问他建议如何强制数据库只能通过他的应用程序进行更新。

你的解决方案比他的好一千倍。仍然“更好”(从涉及避免所有空值的关系纯度的角度来看)是为每种类型的请求提供自己的表:

VehicleQueriesByVIN

user_id(fk)|状态|开始时间|VIN

VehicleQueriesByPlate

user_id(fk)|status|start_time|plate|state

如果要为每个查询保留一段时间内的状态历史跟踪,则必须在其自己的表中单独列出这些内容。

【讨论】:

    【解决方案2】:

    一句话:没有。这是一个错位优化的案例。由于存储字符串,他的架构实际上平均会占用 更多 空间;当然,更复杂的代码和查询的性能会更差。

    【讨论】:

      【解决方案3】:

      将其视为识别车辆的多种方法。您的车辆有一个或多个身份。当地警察可能会使用 LPN 识别您的车辆,而停车当局可能会使用许可证徽章或主动/被动转发器,此外,dmv 可能依赖于 vrn 号码。

      如果您真的想构建一种灵活的方式来将车辆绑定到多个身份,我会使用身份类型表,以便车辆可以拥有一个或多个身份。

      VehicleIdentity
      车辆识别PK
      车辆 ID FK
      身份值
      IdentityType(类型)
      国号??

      车辆
      车牌PK

      我通过删除我认为没有用的表来更新答案:)

      【讨论】:

        【解决方案4】:

        空值很好。它们对于单表继承特别有用,如果您的系统需要“草稿”实体。

        如果您使用像 Postgres 这样的优质数据库,则不会对空值造成存储损失。

        无论如何,如果问题是“我们需要 A OR B,并且 A 和 B 非常相似”,那么答案几乎总是表继承。如果您想快速移动,请使用单表继承。如果 NULL 让您感到难过,请使用类表继承。

        --STI:
        create table vehicle_identifiers (
          id int primary key,
          type text not null check (type in ( 'VIN', 'STATE_N_PLATE' ) )
          vin null,
          state char(2) null,
          plate text null,
        
          check ( ( type='VIN' and vin is not null ) or ( type='STATE_N_PLATE' and state is not null and plate is not null ) )
        );
        
        --CTI:
        create table vehicle_identifiers (
          id int primary key
        );
        
        create table vehicle_identifiers_vin (
          id int primary key references vehicle_identifiers(id),
          vin text not null
        );
        
        create table vehicle_identifiers_state_n_plate (
          id int primary key references vehicle_identifiers(id),
          state text not null,
          plate text not null
        );
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-09-18
          • 1970-01-01
          • 1970-01-01
          • 2018-05-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多