这个问题是关于像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
答案 0 :(得分:1)
理想情况下,Farm
和Factory
应该合并到Workplace
,然后Workplace
可以有一个名为Type
的附加属性,可以取值农场或工厂。所以表格看起来有点像这样:
Workplace
---------
Id Name Salary Type
-- ---- ------ ----
1 BMW 20 Factory
2 Cattle Farm 5 Farm
.
.
你的问题的根源是糟糕的设计,而不是缺乏足够的技巧来编写足够的查询。
现在问题已解决,您无法合并Farm
和Factory
,回答您的问题:
如果我不想合并工厂和农场表,是否有更好的方法来构建这些数据?
无论您构建数据,只要表不同,您就必须编写逻辑来确定要查询的表。因此,如果您将Farm
和Factory
分开并且遵循正确的设计,则卷积是不可避免的。但你可以在幕后隐藏这个卷积。从查询中创建您必须经常加入的表的视图。现在,不是在三个表之间执行查询,而是在一个表和从另一个表的查询生成的视图之间编写查询。
答案 1 :(得分:1)
这通常在modeling OO hierarchies时出现,并且是表继承模式。一般来说,我更喜欢Concrete Table Inheritance而不是Single Table Inheritance,但他们都有自己的用例。
单表继承非常简单 - 只需将Farm
和Factory
表合并到一起即可将它们合并为一个超集。做完了。不幸的是,那些像限制这样的细节变得困难,你会发现自己在鉴别器列上写了很多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
列包含在任何联接中