如何表示具有依赖于其他列的意义的数据?

How to represent data with meaning dependent on other columns?

这个问题是关于像 postgresql 或 oracle 这样的关系数据库的。以下是我的问题的玩具示例。假设我有五个 tables

Client (id, name)
1 Joe
2 Ted

Factory (id, name, salary)
1 BMW     20
2 Porsche 30

Farm (id, name, salary)
1 Wineyard    10
2 Cattle farm  5

Occupation
1 Farmer
2 Worker

Client_Occupation (client-id, occupation-id, dependent-id)
1 (Joe) 1 (Farmer) 1 (Wineyard)
2 (Ted) 2 (Worker) 2 (Porsche)

要了解 Joe 的收入,sql 需要使用来自 table 农场(如果职业 ID 为 1)或工厂(如果职业 ID 为 2)的数据.这会创建非常复杂的查询,并使处理代码比应有的更复杂。

如果我不想合并工厂和农场 table,是否有更好的方法来构建这些数据?


换句话说:table client_ocupation 与农场或工厂有条件关系。有没有更好的方式来表示这些信息?

                 Client
                   ^
                   |
Factory <-  Client_Occupation -> Farm
                   |
                   v
               Occupation

理想情况下,FarmFactory 应该合并到 Workplace,然后 Workplace 可以有一个名为 Type 的额外属性,它可以取值 农场工厂。所以 table 看起来有点像这样:

Workplace
---------

Id  Name         Salary  Type
--  ----         ------  ----
1   BMW          20      Factory
2   Cattle Farm  5       Farm
.
.

你的问题的根源是糟糕的设计,而不是缺乏编写足够好的查询的技巧。


现在解决了FarmFactory不能合并的问题,回答你的问题:

Is there a better way to structure this data if I do not want to merge the factories and farms tables?

无论您如何构建数据,只要 table 不同,您就必须编写逻辑来确定查询哪个 table。因此,如果您将 FarmFactory 分开并遵循适当的设计,那么 卷积 是必然的 table。但是你可以在幕后隐藏这个卷积。根据您必须经常加入的 table 的查询创建视图。现在不是在三个 table 之间执行查询,而是在一个 table 和从其他两个查询生成的视图之间编写查询。

这在 modeling OO hierarchies, and is a table inheritance pattern. Generally, I prefer Concrete Table Inheritance over Single Table Inheritance 时经常出现,但它们都有各自的用例。

单一 Table 继承非常简单 - 只需将您的 FarmFactory table 合并到一个超集即可。完成了。不幸的是,像约束这样的细节变得困难,你会发现自己在鉴别器列上写了很多 CASE 表达式。

具体 Table 继承需要一点时间来理解,但实际上也很简单。您设计一个基本 table w/common 属性和一个鉴别器列,然后为层次结构的每个级别设计 "sub" tables w/specific 属性。子 table 通过 Id 和鉴别器链接回基础 table - 这提供了一些查询优化器收益并防止单个实体成为多种类型。

对于您的示例问题,叶子上确实没有任何特定属性 - 所以这看起来有点奇怪,但该模式仍然有效(请注意,我将 "dependent-id" 重命名为 "location-id" 使其更易于理解):

Occupation (id, name)
1 Farmer
2 Worker

# the base table for all locations w/common attributes
Location (id, type, name, salary)
# unique constraint on discriminator column, and another on the combination of id and type so that we can reference in a foreign key
PK(id), UC(type), UC(id, type)
1 Farm Wineyard 10
2 Farm Cattle farm 5
3 Factory BMW 20
4 Factory Porsche 30

Client_Occupation (client-id, occupation-id, location-id)
FK location-id => location.id
1 (Joe) 1 (Farmer) 3 (Wineyard)
2 (Ted) 2 (Worker) 1 (Porsche)

Farm
# carry the discriminator column and check constraint it; any Farm specific columns can be added here
PK(id), FK(id, type) => location(id, type), CHECK type = 'Farm'
Factory (id, type)
1 Farm
2 Farm

Factory
# carry the discriminator column and check constraint it; any Factory specific columns can be added here
PK(id), FK(id, type) => location(id, type), CHECK type = 'Factory'
Factory (id, type)
3 Factory
4 Factory

您现在可以轻松获得所有适用的薪水:

SELECT * FROM Client_Occupation CO JOIN Location L ON CO.location-id = L.id

并且,如果您想获取特定的 Farm 列,或将其限制在 Farm 个位置:

SELECT * FROM Client_Occupation CO JOIN Location L ON CO.location-id = L.id
JOIN Farm F ON CO.id = F.id and CO.type = F.type // technically, joining on type is unnecessary, but I prefer to include it anyway

而且,如果您想模拟单一 Table 继承模型:

SELECT * FROM Client_Occupation CO JOIN Location L ON CO.location-id = L.id
LEFT OUTER JOIN Farm F ON CO.id = F.id and CO.type = F.type 
LEFT OUTER JOIN Factory T ON CO.id = T.id and CO.type = T.type 

有时,很难将现有的 table 设计重构到此模式中。在这种情况下,基础 table 的视图提供了很多相同的查询优势:

CREATE VIEW Location AS
   SELECT id, 'Farm' as type, name, salary FROM Farm
   UNION ALL
   SELECT id, 'Factory' as type, name, salary FROM Factory

到那时,您将(可能)有冲突的 ID(例如,FarmId = 1 和 FactoryId = 1),因此您需要努力在任何连接中包含 type 列.