只有在根 table 中具有特定值时,我才能建立 table 关系吗?
Can I make a table relation only if in the root table has a specific value?
在 Table DEVICE 中我有一个 TYPE,如果 TYPE 是 HEATER 则与 HEATER table 建立关系,否则如果 TYPE 是 LAMP 与LAMP table。这样的事情可以做吗?
@MikeNakis 所以这应该是不可能的:
Device table:
+-----+----------+----------+
| Id | LampId | HeaterId |
+-----+----------+----------+
| 1 | NULL | 1 |
| 2 | 2 | 2 |
| 3 | 3 | NULL |
| 4 | NULL | 4 |
+-----+----------+----------+
Lamp table:
+-----+----
| Id |
+-----+----
| 2 |
| 3 |
+-----+----
Heater table:
+-----+----
| Id |
+-----+----
| 1 |
| 2 |
| 4 |
+-----+----
是的,可以这样做。
这是对象关系映射 (ORM) 框架的工作方式之一(在我看来是最好的方式)。这不是巧合,因为你在这里试图实现的是一个映射到关系数据库的面向对象的继承层次结构。 "Device" 是基础 class,而 "Lamp" 和 "Heater" 是派生(后代)classes.
操作方法如下:
删除 Type
列。正如我将进一步展示的那样,它将成为一个计算列。
而不是 Type
列,引入一对所谓的 "descendant-id" 列:一个 HeaterId
列和一个 LampId
列。如果您愿意,可以添加一个约束,即只允许这些后代 ID 列中的一个为非空。如果您以后添加更多设备,则必须添加更多后代 ID 列。
然后,您可以使 Type
成为计算列,基于其中一个后代 ID 列包含非 NULL 值。
坏消息是您将无法设置 Type
列的值,但这没关系;您将通过将非空值存储到后代 ID 列之一来隐式定义类型,而不是设置 Type
。
注意:在面向对象的编程中,很少会设计一个基 class 以便它对它的后代有任何了解。有些人甚至会说,拥有这样的知识是荒谬的。我不会那么绝对,我遇到过一些小的、紧密结合的、不可扩展的 class 图表的例子,在这些图表中,这些知识是合法且有用的。但是在任何情况下,即使您严格遵守基数 class 永远不应该知道其后代的任何信息的格言,这也不完全是面向对象的编程:这是关系数据库设计。这是将 class 图映射到数据库所必须付出的代价。
进一步说明:
首先,一些术语:"long row" 是一对父(base/ancestor)行加上后代行,一起检查。 "row fragment" 只是其中之一,单独检查。
每个后代行片段都必须与其父行片段具有完全相同的主键 ID。所以,这是一个示例结构:
Device table:
+-----+----------+----------+
| Id | LampId | HeaterId |
+-----+----------+----------+
| 1 | NULL | 1 |
| 2 | 2 | NULL |
| 3 | 3 | NULL |
| 4 | NULL | 4 |
+-----+----------+----------+
Lamp table:
+-----+----
| Id |
+-----+----
| 2 |
| 3 |
+-----+----
Heater table:
+-----+----
| Id |
+-----+----
| 1 |
| 4 |
+-----+----
这意味着在基行片段中,一个非空的后代 ID 始终与同一行的主键具有完全相同的值。
这种方式除了非常容易排除故障外,还有一个额外的性能优势:将一整行插入数据库不需要任何额外的数据库往返:您使用序列或自动增加值以仅发布一个 id,这是参与插入的所有行片段所需的唯一 id。即使您的继承层次结构比两层更深,这也很有用。
在 Table DEVICE 中我有一个 TYPE,如果 TYPE 是 HEATER 则与 HEATER table 建立关系,否则如果 TYPE 是 LAMP 与LAMP table。这样的事情可以做吗?
@MikeNakis 所以这应该是不可能的:
Device table:
+-----+----------+----------+
| Id | LampId | HeaterId |
+-----+----------+----------+
| 1 | NULL | 1 |
| 2 | 2 | 2 |
| 3 | 3 | NULL |
| 4 | NULL | 4 |
+-----+----------+----------+
Lamp table:
+-----+----
| Id |
+-----+----
| 2 |
| 3 |
+-----+----
Heater table:
+-----+----
| Id |
+-----+----
| 1 |
| 2 |
| 4 |
+-----+----
是的,可以这样做。
这是对象关系映射 (ORM) 框架的工作方式之一(在我看来是最好的方式)。这不是巧合,因为你在这里试图实现的是一个映射到关系数据库的面向对象的继承层次结构。 "Device" 是基础 class,而 "Lamp" 和 "Heater" 是派生(后代)classes.
操作方法如下:
删除 Type
列。正如我将进一步展示的那样,它将成为一个计算列。
而不是 Type
列,引入一对所谓的 "descendant-id" 列:一个 HeaterId
列和一个 LampId
列。如果您愿意,可以添加一个约束,即只允许这些后代 ID 列中的一个为非空。如果您以后添加更多设备,则必须添加更多后代 ID 列。
然后,您可以使 Type
成为计算列,基于其中一个后代 ID 列包含非 NULL 值。
坏消息是您将无法设置 Type
列的值,但这没关系;您将通过将非空值存储到后代 ID 列之一来隐式定义类型,而不是设置 Type
。
注意:在面向对象的编程中,很少会设计一个基 class 以便它对它的后代有任何了解。有些人甚至会说,拥有这样的知识是荒谬的。我不会那么绝对,我遇到过一些小的、紧密结合的、不可扩展的 class 图表的例子,在这些图表中,这些知识是合法且有用的。但是在任何情况下,即使您严格遵守基数 class 永远不应该知道其后代的任何信息的格言,这也不完全是面向对象的编程:这是关系数据库设计。这是将 class 图映射到数据库所必须付出的代价。
进一步说明:
首先,一些术语:"long row" 是一对父(base/ancestor)行加上后代行,一起检查。 "row fragment" 只是其中之一,单独检查。
每个后代行片段都必须与其父行片段具有完全相同的主键 ID。所以,这是一个示例结构:
Device table:
+-----+----------+----------+
| Id | LampId | HeaterId |
+-----+----------+----------+
| 1 | NULL | 1 |
| 2 | 2 | NULL |
| 3 | 3 | NULL |
| 4 | NULL | 4 |
+-----+----------+----------+
Lamp table:
+-----+----
| Id |
+-----+----
| 2 |
| 3 |
+-----+----
Heater table:
+-----+----
| Id |
+-----+----
| 1 |
| 4 |
+-----+----
这意味着在基行片段中,一个非空的后代 ID 始终与同一行的主键具有完全相同的值。
这种方式除了非常容易排除故障外,还有一个额外的性能优势:将一整行插入数据库不需要任何额外的数据库往返:您使用序列或自动增加值以仅发布一个 id,这是参与插入的所有行片段所需的唯一 id。即使您的继承层次结构比两层更深,这也很有用。