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

时间:2016-02-20 22:52:00

标签: database entity-relationship

这个问题是关于像postgresql或oracle这样的关系数据库。以下是我的问题的玩具示例。说我有五张桌子

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需要使用表农场(如果职业ID为1)或工厂(如果职业ID为2)的数据。这会产生非常复杂的查询,并使处理代码更加复杂。

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

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

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

2 个答案:

答案 0 :(得分:1)

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

Workplace
---------

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

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

现在问题已解决,您无法合并FarmFactory,回答您的问题:

  

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

无论您构建数据,只要表不同,您就必须编写逻辑来确定要查询的表。因此,如果您将FarmFactory分开并且遵循正确的设计,则卷积是不可避免的。但你可以在幕后隐藏这个卷积。从查询中创建您必须经常加入的表的视图。现在,不是在三个表之间执行查询,而是在一个表和从另一个表的查询生成的视图之间编写查询。

答案 1 :(得分:1)

这通常在modeling OO hierarchies时出现,并且是表继承模式。一般来说,我更喜欢Concrete Table Inheritance而不是Single Table Inheritance,但他们都有自己的用例。

单表继承非常简单 - 只需将FarmFactory表合并到一起即可将它们合并为一个超集。做完了。不幸的是,那些像限制这样的细节变得困难,你会发现自己在鉴别器列上写了很多CASE个表达式。

具体表继承需要一些时间来理解,但实际上也非常简单。您设计了一个具有公共属性和鉴别器列的基表,然后是&#34; sub&#34;表w /层次结构的每个级别的特定属性。子表通过Id和Discriminator链接回基表 - 它提供了一些查询优化器增益并防止单个实体成为多种类型。

对于你的示例问题,你真的没有叶子上的任何特定属性 - 所以这看起来有点奇怪,但模式仍然有效(注意我重命名&#34;依赖-id&#34;到&#34; location-id&#34;使其更容易理解):

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

并且,如果您想模拟单表继承模型:

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 

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

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列包含在任何联接中