在没有太多列名的情况下存储有关模型的额外信息的策略(DB规范化和模型子类化的替代方案)



假设您有一个名为Forest的模型。每一个物体都代表着你大陆上的一片森林。所有这些林都有一组通用的数据,如林类型、面积等,这些数据可以很容易地用SQL表上的列forest表示。

然而,想象一下,这些森林有关于它们的额外数据,这些数据可能并不总是可重复的。例如,20个针叶林具有pine-fir分裂比数,而落叶林具有autumn-duration数。一种方法是将所有这些列存储在主表本身上,但每行上的列太多,许多列仍未按定义填充。

最明显的解决方法是创建Forest模型的子类,并为每个子类提供单独的表。我觉得这是一种高压手段,我宁愿不遵循。如果我需要一些关于通用林的数据,我必须查阅另一张表。

有解决这个问题的模式吗?你通常喜欢什么解决方案?

注意:我已经看到了关于这个的其他问题。提出的解决方案是:

  • 子类型,与我上面提出的相同
  • 将所有列放在同一张表上
  • 每种森林都有单独的表格,有重复的数据,如面积和降雨量。。。重复

有没有我不知道的创造性解决方案?

UPDATE:我已经运行了EAV模型,还有一个修改后的版本,其中不可预测的字段存储在NoSQL/JSON存储中,其id保存在RDB中。我两者都喜欢,但欢迎在这方面提出建议。

在数据库方面,最好的方法通常是将所有林的公共属性存储在一个表中,并将唯一属性存储在其他表中。生成可更新的视图供客户端使用。

create table forests (
forest_id integer primary key,
-- Assumes forest names are not unique on a continent.
forest_name varchar(45) not null,
forest_type char(1) not null 
check (forest_type in ('c', 'd')),
area_sq_km integer not null
check (area_sq_km > 0),
-- Other columns common to all forests go here.
--
-- This constraint lets foreign keys target the pair
-- of columns, guaranteeing that a row in each subtype
-- table references a row here having the same subtype.
unique (forest_id, forest_type)
);
create table coniferous_forests_subtype (
forest_id integer primary key,
forest_type char(1) not null
default 'c'
check (forest_type = 'c'),
pine_fir_ratio float not null
check (pine_fir_ratio >= 0),
foreign key (forest_id, forest_type)
references forests (forest_id, forest_type)
);
create table deciduous_forests_subtype (
forest_id integer primary key,
forest_type char(1) not null
default 'd'
check (forest_type = 'd'),
autumn_duration_days integer not null
check (autumn_duration_days between 20 and 100),
foreign key (forest_id, forest_type)
references forests (forest_id, forest_type)
);

客户端通常使用可更新的视图,每个子类型一个,而不是使用基表。(您可以撤消对基子类型表的权限以保证这一点。)您可能需要省略"forest_type"列。

create view coniferous_forests as 
select t1.forest_id, t1.forest_type, t1.area_sq_km,
t2.pine_fir_ratio
from forests t1
inner join coniferous_forests_subtype t2
on t1.forest_id = t2.forest_id;
create view deciduous_forests as 
select t1.forest_id, t1.forest_type, t1.area_sq_km,
t2.autumn_duration_days
from forests t1
inner join deciduous_forests_subtype t2
on t1.forest_id = t2.forest_id;

您必须做些什么才能使这些视图可更新,这与dbms略有不同,但需要编写一些触发器(未显示)。您需要触发器来处理所有DML操作——插入、更新和删除。

如果只需要报告"林"中出现的列,那么只需查询表"林"即可。

好吧,最简单的方法是将所有列放在一个表中,然后有一个"type"字段来决定使用哪些列。这适用于较小的表,但对于更复杂的情况,它可能会导致大的混乱表和数据库约束(如NULL)问题。

我喜欢的方法是这样的:

A generic "Forests" table with:  id, type, [generic_columns, ...]
"Coniferous_Forests" table with: id, forest_id (FK to Forests), ...

因此,为了获得id为1的针叶林的所有数据,您需要这样的查询:

SELECT * FROM Coniferous_Forests INNER JOIN Forests 
ON Coniferous_Forests.forest_id = Forests.id
AND Coniferous_Forests.id = 1

至于创造性的解决方案,存在OODBMS(面向对象数据库管理系统)这样的东西。

关系SQL数据库最流行的替代方案是面向文档的NoSQL数据库,如MongoDB。这与使用JSON对象存储数据相当,并允许您更灵活地使用数据库字段。

相关内容

最新更新