Database 保证为空字段是否表示数据库设计不佳?

Database 保证为空字段是否表示数据库设计不佳?,database,relational-database,database-schema,Database,Relational Database,Database Schema,我正在开发一个批处理应用程序,允许用户提交有关特定车辆信息的请求。用户可以使用VIN或车牌/州组合提交请求。我提出了以下表格结构: 已处理的车辆底座 车辆id(fk)|用户id(fk)|状态|启动时间 车辆 车辆id | VIN |车牌|状态 我的同事认为这是一个糟糕的设计,因为车辆中的每条记录要么有一个空VIN字段,要么有空车牌和状态字段。相反,他们提出了以下建议: 已处理的车辆底座 车辆id(fk)|用户id(fk)|状态|启动时间 车辆 车辆识别码(pk)|字段|值 如果车辆中的条目

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


已处理的车辆底座

车辆id(fk)|用户id(fk)|状态|启动时间

车辆

车辆id | VIN |车牌|状态

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

车辆id(fk)|用户id(fk)|状态|启动时间

车辆

车辆识别码(pk)|字段|值

如果车辆中的条目包含一行vin:

1 |“vin”|“123

或一个板/状态的两行:

2 |“plate”|“abc 123”
2 |“州”|“纽约”



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

一句话:不。这是一种错位优化的情况。由于存储strin,他的模式实际上平均会占用更多空间当然,更复杂的代码和查询会有更差的性能。

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

如果你真的想建立一种灵活的方式将一辆车绑定到多个身份,我会使用一个身份类型表,这样一辆车就可以有一个或多个身份

车辆身份
车辆识别PK
车辆ID FK
识别值
标识类型(类型)
StateID

车辆
车辆ID PK


我更新了答案,删除了一个我认为没有用的表:)

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

谷歌搜索比尔·卡温的《反模式》一书和《EAV》

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

您的解决方案比他的好上千倍。但“更好”(从关系纯度的角度来看,它包括避免所有空值)是为每种类型的请求提供自己的表:

车辆查询拜文

用户id(fk)|状态|开始|时间| VIN

汽车牌照

用户id(fk)|状态|开始|时间|车牌|状态


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

空值很好。它们对于单表继承以及系统需要“草稿”实体特别有用

如果您使用像Postgres这样的高质量数据库,那么空值不会导致存储损失

不管怎么说,如果问题是“我们需要A或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
);

我不确定一个车牌/状态应该如何使用两行。如果
vehicle\u id(pk)
是一个主键,那么你不能有两行相同的车牌号。而且,据我所知,每辆车都必须有VIN(可能是未知的),但没有车牌号。此外,车牌号会随着时间的推移而变化(可能会变化)。