数据库:何时拆分成单独的表?

时间:2010-10-05 13:09:48

标签: database database-design

假设我有两种不同类型的传感器:一种监测模拟电压(例如温度传感器),一种测量是否开启或关闭(开关传感器)。

我无法决定是否有一张桌子:

[Sensor]
Id : PK
UpperLimit : FLOAT
UpperLimitAlertDelay : INT
LowerLimit : FLOAT
LowerLimitAlertDelay : INT
IsAnalog : BOOL

[SensorReading]
Id : PK
SensorId : FK
AnalogValue : FLOAT
IsOn : BOOL

或者将它们全部分成不同的表格:

[AnalogSensor]
Id : PK
UpperLimit : FLOAT
UpperLimitAlertDelay : INT
LowerLimit : FLOAT
LowerLimitAlertDelay : INT

[AnalogSensorReadings]
Id : PK
AnalogSensorId : FK
Value : FLOAT

[SwitchSensor]
Id : PK
OnTooLongAlertDelay : INT

[SwitchSensorReadings]
Id : PK
SwitchSensorId : FK
IsOn : BOOL

目前,我把它作为一个表,当不将它用作模拟传感器时,我将“UpperLimitAlertDelay”用作“OnTooLongAlertDelay”。

在代码中,我通过传感器表上的布尔标志进行区分,并创建适当的对象(即AnalogSensor或SwitchSensor),但我想知道它是否在数据库级别更整洁/更合适以将其分离出来。

您将使用哪种经验法则作出此类决定?它们在一个层面上是不同的实体,但在另一个层面上,您可以说它们都只是传感器。

这通常是我无法决定创建数据库时要采取的方向。也许每当我使用bool来区分哪些字段意味着/应该使用时,它应该是一个单独的表?

关于这个主题或这个特殊问题的一般想法。

谢谢!

编辑:更多信息。

开关传感器监控门是否打开,冰箱压缩机是否正在运行,设备是否已打开等情况。

可以在任何传感器上生成图表和报告,以便以相同的方式使用它们;它只是数据将打开/关闭或模拟值取决于类型。

所以基本上它们通常都是一样的。

在读数表中,总是一行读取一个传感器。

到目前为止,这些意见似乎是主观的 - 我猜两种方式都有利有弊。

上述信息是否会改变任何人的意见?

谢谢! 标记

7 个答案:

答案 0 :(得分:2)

表格通常被拆分为逻辑上不同的“ thing ”,因此您没有两次相同的“ things ”。例如,您不希望:

[SensorReadings]
Id : PK
UpperLimit : FLOAT
UpperLimitAlertDelay : INT
LowerLimit : FLOAT
LowerLimitAlertDelay : INT
IsAnalog : BOOL
AnalogValue : FLOAT
IsOn : BOOL

因为您将传感器读数混合成一行。传感器与其读数不同

[Sensors]                      [SensorReadings]
Id                             Id
UpperLimit                     SensorID
UpperLimitAlertDelay           Reading
LowerLimit
LowerLimitAlertDelay
IsAnalog
Manufacturer
SerialNumber
LastInspectionDate
...

我不会这样做的一件事就是将“传感器”分成两个表。传感器是一种传感器,就是这样的传感器。就像客户是客户,或者歌曲是一首歌。你会有一张歌曲表,而不是一张古典歌曲表和另一张表。如果将传感器分成两个表,则可以设想两个传感器具有相同的ID。传感器是独特的实体,它们应该在同一个表中,并且都具有唯一的ID。传感器是模拟的或数字的这一事实是传感器的特性。


您的问题是独一无二的 - 您的传感器可以使用不同逻辑格式的 读数 ;一些是模拟浮点值,另一些是数字布尔值。当并非所有传感器读数都适合相同的逻辑列数据类型(即float vs bool)时,您正在努力存储传感器的“读数”。它归结为实用性,对系统最有利。

您可以将所有读数存储在浮点数列中:

[SensorReadings]
Id  SensorID  Reading
==  ========  =======
 1   3728     120.2
 2   3728     120.3
 3     89         1
 4     89         0
 5   3728     120.2
 6     89         0

但是现在您必须知道将01的浮点值解释为逻辑onoff。难道难道吗?我个人并不这么认为。没错,它没有充分利用数据库引擎中可用的数据类型,但我并不在乎。您将SensorReadings加入Sensors,因此您可以使用IsAnalog列来帮助您解释。换句话说:

SELECT Id, SensorID, Reading, Sensors.IsAnalog
FROM SensorReadings sr
   INNER JOIN Sensors s ON sr.SensorID = s.SensorID

将为您提供一个非常容易解析的结果集:

Id  SensorID  Reading  IsAnalog
==  ========  =======  ========
 1   3728     120.2     false
 2   3728     120.3     false
 3     89         1      true
 4     89         0      true
 5   3728     120.2     false
 6     89         0      true

你甚至可以创建一个助手视图(或只是一个查询),将阅读解码为AnalogReadingDigitalReading

CREATE VIEW SimpleSensorReadings AS

SELECT Id, SensorID, Reading AS RawReading,
    CASE Sensors.IsAnalog
    WHEN 0 THEN Reading
    ELSE NULL
    END AS AnalogReading,

    CASE Sensors.IsAnalog
    WHEN 1 THEN CAST(Reading AS BOOL)
    ELSE NULL
    END AS DigitalReading,
    Sensors.IsAnalog
FROM SensorReadings sr
   INNER JOIN Sensors s ON sr.SensorID = s.SensorID

这会给你:

[SimpleSensorReadings]
Id  SensorID  RawReading  AnalogReading  DigitalReading  IsAnalog
==  ========  ==========  =============  ==============  ========
 1   3728        120.2         120.2                       true
 2   3728        120.3         120.3                       true
 3     89            1                       true         false
 4     89            0                      false         false
 5   3728        120.2         120.2                       true
 6     89            0                      false         false

这取决于谁必须处理结果。我可以很容易地想象代码首先检查“IsAnalog”列,然后根据需要读出AnalogReadingDigitalReading


你可以做你最初的建议;将它们分成多个表。但现在问题变成了:你如何访问数据?在我看来,如果我有这个传感器读数系统,在某些时候我将不得不对它们做些什么 - 向用户展示它们。现在我必须跳过箍重新加入数据:

SELECT ID, AnalogSensorID AS SensorID, 
   Value AS RawReading, Value AS AnalogReading, 
   true AS IsAnalog
FROM AnalogSensorReadings

UNION ALL 

SELECT ID, SwitchSensorID AS SensorID, 
   CAST(IsOn AS float) AS RawReading, null AS AnalogReading, IsOn AS DigitalReading,
   false AS IsAnalog

给你

Id  SensorID  RawReading  AnalogReading  DigitalReading  IsAnalog
==  ========  ==========  =============  ==============  ========
 1   3728        120.2         120.2                       true
 2   3728        120.3         120.3                       true
 1     89            1                       true         false
 2     89            0                      false         false
 3   3728        120.2         120.2                       true
 3     89            0                      false         false

除了现在“Id”也难以解码,因为两个不同的读数可以具有相同的“ID”。阅读是一种阅读,应该是独一无二的。

您可能正在寻找的折衷方案就是您最初拥有的。

[SensorReadings]
Id  SensorID  AnalogReading  DigitalReading
==  ========  =============  ==============
 1   3728        120.2         
 2   3728        120.3         
 3     89                       true
 4     89                      false
 5   3728        120.2         
 6     89                      false

是的,这会给你留下很多(null)值 - 但是将表连接在一起的费用是一个实际问题,必须考虑到你的设计决策。


我认为它就像Windows中的注册表。 key包含value。您并不真正关心如何存储该值,只要您可以读取它,因为该类型在逻辑上是。为了在数据库中实现这一点,我将使用多个数据类型列,并在适当时读取它们。

答案 1 :(得分:2)

这是与your other question相同的应用程序/数据库吗?

在这种情况下,答案已在Data Model中提供。

如果它不是相同的应用程序/数据库,或者此问题尚未得到充分回答,请发布或评论。例如。根据以前的信息,我对其建模,以便SensorType表区分Sensor(模拟或布尔)......但我们可以:

  • 在传感器级别区分它,

  • 或将Reading变为子类型:ReadingAnalogReadingSwitch。这可以使生成图表等的程序更容易。

答案 2 :(得分:1)

通常,您希望尽可能减少数据库设计中的冗余。去查看Normal Forms,BCNF以下的任何内容通常很难维护。某些应用程序使用更多冗余来实现更高的读取性能,但却牺牲了清晰度和写入性能,例如数据仓库。 连接可能很慢,但是当相同的信息存储两次时,它们比不一致的数据更好。

因此我建议使用较低的一个。假设您的传感器不再与完美的时间戳相关联:突然,第一个布局建议失败了。

答案 3 :(得分:1)

问题是:从您的系统的角度来看,它们是一回事吗?如果是这样,它们属于一个表。如果没有,它们属于两个表格。

通常这是一个明智的选择。 “员工”和“保险计划”是两回事。 “名为Bob的员工”和“名为Sally的员工”是同一事件的两个实例。

有时它比较棘手。 “卡车”和“船”是两个不同的东西,还是它们都只是“车辆”的子类型?这取决于您的系统的观点。如果你卖它们,它们可能是同一件事。你可能不关心那个漂浮而另一个不漂浮,你只关心它们的成本和库存等数量。也就是说,您保留有关它们的相同数据,并在相同的查询中使用它们。但是如果你的系统管理一个捕鱼船队和船只你关心的事情,如船员是谁,他们有多少报酬,他们今天捕获了多少鱼,而对于卡车,你关心的事情,如它会出现在码头拿起今天的渔获量以及每磅运输公司需要付多少钱,它们可能是两种截然不同的东西。

当然有迹象表明它们是相同的:

  • 他们拥有相同的数据(当然不是相同的值,但字段相同)
  • 通常会在很少或没有区别的情况下对查询进行查询

如果不是这样,那么它们可能不是一回事。

也就是说,如果您发现类型1,字段A将具有值,字段B将始终为空,而对于类型2,字段A将为空,字段B将具有值,然后它们将失败数据测试。

如果您发现如果将它们放在一个表中,则通常需要对所有查询添加类型检查,这样您才能获得正确的查询,然后它们无法通过数据测试。如果您得出结论,如果将它们放在两个单独的表中,则必须不断编写在两个表上执行连接或联合的查询以获取两者,然后它们通过数据测试。

在您的示例中,您尚未告诉我们您打算如何处理数据,因此我无法讨论查询测试。显然,对于两种类型的传感器,您有不同的数据 - 数字与开/关 - 因此,这是对两种不同事物的投票。但接下来我们回到这对你的系统来说真的很重要。如果对于温度探测器,您将生成温度随时间变化的图形或监视它们是否在一定范围内,对于开/关开关,当它们继续时会触发一个过程并在它们熄灭时停止它们,它们可能是两个不同的的东西。如果在这两种情况下,您将在任何给定时间生成价值报告 - 无论是数字还是开/关 - 那么它们可能是相同的。

我倾向于认为他们可能不同,但我不知道更多,我真的不能说。

答案 4 :(得分:0)

我建议根据normalisation rules行事。

根据您的需要,您可以选择no-sql数据库。

答案 5 :(得分:0)

这是一个相当标准的设计决策,需要在执行对象关系映射时做出。

您提供的第一个选项称为table-per-hierarchy,第二个选项是table-per-concrete-class。还有其他变体,例如将抽象类映射到它们自己的表。一些O-R框架(例如hibernate)提供了现成的代码来实现其中的一些模式。

在某些搜索中使用其中一些关键字应该会为您提供更多信息。需要考虑很多权衡因素。我想最先想到的是你可能有多少种不同类型的传感器。

需要考虑的另一件事是报道。如果您要编写大量查询所有类型传感器的报告,那么每个类的表将需要联合,这可能是不合需要的。

答案 6 :(得分:0)

我怀疑它将取决于与未显示的其他实体的关系。如果有很多实体与一种类型的传感器相关而不是另一种传感器,那么将它们分开可能是有意义的 - 否则,我倾向于使用更简单的设计(即两种表格方法,而不是比四表方法)。

我建议做一些改变:

  1. 将“UpperLimitAlertDelay”和“OnTooLongAlertDelay”分成不同的字段 - 据我所知,它们是不同的值,因此(在1NF以下)应该是单独的字段。
  2. 将datetimestamp字段添加到Reading表中。