11

假设您正在收集有关即将上映的超级英雄电影的内幕信息,并且您的主电影表如下所示:

表格1

Title              Director   Leading Male      Leading Female    Villain
--------------------------------------------------------------------------
Green Lantern      Kubrick    Robert Redford     Miley Cyrus     Hugh Grant  
The Tick          Mel Gibson  Kevin Sorbo        Linda Hunt    Anthony Hopkins

一般来说,这应该工作得很好,并且允许非常简单的查询以及行之间的比较。

但是,您想跟踪每个数据事实的来源,以及发现该事实的记者的姓名。这似乎暗示了某种像这样的EAV表:

表 2

Movie             Attribute            Value          Source          Journalist
----------------------------------------------------------------------------------
Green Lantern      Director           Kubrick         CHUD              Sarah
Green Lantern    Leading Male      Robert Redford     CHUD              James
Green Lantern   Leading Female      Miley Cyrus    Dark Horizons        James
Green Lantern      Villain           Hugh Grant       CHUD              Sarah
The Tick           Director          Mel Gibson       Yahoo            Cameron
...

虽然它很容易捕获我们想要的元数据,但它使查询变得更加困难。简单地获取一部电影的所有基本数据需要更多的时间。更具体地说,您必须在此处处理四行才能获得有关绿灯侠的四个重要信息,而在表 1 中它是一个单独的、很好封装的行。

所以我的问题是,鉴于我刚刚描述的复杂性,并且因为我知道通常要避免使用 EAV 表,EAV 仍然是最好的解决方案吗?它似乎是表示这些数据的唯一合理方式。我看到的唯一另一种选择是将表 1 与另一个包含如下元数据的表结合使用:

表3

Movie             Attribute            Source          Journalist
----------------------------------------------------------------------------------
Green Lantern      Director             CHUD              Sarah
Green Lantern    Leading Male           CHUD              James
Green Lantern   Leading Female      Dark Horizons         James
Green Lantern      Villain              CHUD              Sarah
The Tick           Director             Yahoo            Cameron
...

但这是非常危险的,因为如果有人将表 1 中的列名更改为“Villain”为“Primary Villain”,那么表 3 中的行仍然会简单地说“Villain”,因此相关数据将不幸解耦。如果“属性”列链接到另一个用作表 1 列枚举的表,这可能会有所帮助。当然,DBA 将负责维护此枚举表以匹配表 1 的实际列。并且实际上,通过使用 SQL Server 中包含表 1 中列名称的系统视图,而不是手动创建枚举表,实际上可能会进一步改进这一点。虽然我不确定你是否可以拥有涉及系统视图。

你有什么建议?EAV是唯一的出路吗?

如果它只有一个元数据列(只有“来源”,没有“记者”)——是否仍然需要走 EAV 路线?你可以有“Director”、“Director_Source”、“Leading Male”、“Leading Male_Source”等列,但这很快就会变得丑陋。有没有更好的解决方案我没有想到?

如果我没有澄清任何观点,请发表评论,我会根据需要添加更多内容。哦,是的,我使用的电影数据是捏造的:)

编辑:为了简明扼要地重申我的主要问题,我希望表 1 具有简单性和真正的 RDBMS 设计,它确实很好地描述了电影条目,同时仍以安全且可访问的方式存储属性上的元数据。这可能吗?还是 EAV 是唯一的方法?

编辑 2:在做了更多的网络研究之后,我还没有找到关于 EAV 的讨论,该讨论围绕着在列上存储元数据的愿望。实现 EAV 的主要原因几乎总是动态且不可预测的列,而在我的示例中并非如此。在我的例子中,总是有相同的四列:导演、男主角、女主角、反派。但是,我想为每一行存储关于每一列的某些事实(来源和记者)。EAV 会促进这一点,但我想避免诉诸于此。

更新

使用表 2 设计,除了将列“Movie”重命名为“Name”并将整个表称为“Movie”之外,这是 SQL Server 2008 中用于取回表 1 的数据透视操作:

SELECT Name, [Director], [Leading Male], [Leading Female], [Villain]
FROM (Select Name, Attribute, Value FROM Movie) as src
PIVOT
(
Max(Value)
FOR Attribute IN ([Director], [Leading Male], [Leading Female], [Villain])
)  AS PivotTable
4

9 回答 9

6

您可以在设计中更改您认为的事实值……您的数据模型中的事实似乎可以表示为以下 N 元组:

Movie | FactType | FactValue | FactSource | FactJournalist

以下表结构应该支持您想要的数据模型,并且可以相对容易地被索引和连接。您还可以创建一个仅以事实值和事实类型为中心的视图,以便您可以创建以下透视图:

MovieID | Movie Name | Director | LeadingMale | LeadingFemale | PrimaryVillain | etc

有趣的是,您可以认为这是将 EAV 模型完全应用于数据并将单个电影(具有导演、主角、恶棍等的直观属性)分解为属性集中于源的枢轴结构的逻辑扩展的信息。

所提出的数据模型的好处是:

  • 它是规范化的(尽管您可能应该将 FactType 字段规范化为参考表以确保完整性)
  • 可以创建一个视图,将事实类型有效地转出到表格结构中
  • 它是相对可扩展的,并允许数据库强制执行引用完整性和(如果需要)基数约束
  • MovieFact 表可以被子类化以支持不同类型的电影事实,而不仅仅是那些简单的文本字段
  • 针对数据的简单查询相对有效

数据模型的一些缺点是:

  • 复合条件查询更难(但并非不可能)编写(例如,查找导演为 A 且男主角为 B 的所有电影,等等...)
  • 该模型与更传统的方法或涉及 EAV 结构的方法相比不太明显
  • 插入和更新有点棘手,因为更新多个事实需要更新多行,而不是多列

我将电影数据提升了一个级别以规范结构,您可以将电影名称下推到 MovieFact 结构中以保持一致性(因为对于某些电影,即使这样我也可以想象名称是您可能想要跟踪源信息的东西) .

Table Movie
========================
MovieID   NUMBER, PrimaryKey
MovieName VARCHAR

Table MovieFact
========================
MovieID          NUMBER,  PrimaryKeyCol1
FactType         VARCHAR, PrimaryKeyCol2
FactValue        VARCHAR
FactSource       VARCHAR
FactJournalist   VARCHAR

您的虚构电影数据将如下所示:

Movie Table
====================================================================================
MovieID  MovieName
====================================================================================
1        Green Lantern
2        The Tick

MovieFact Table
====================================================================================
MovieID  FactType       FactValue         FactSource       FactJournalist
====================================================================================
1        Director       Kubrick           CHUD             Sarah
1        Leading Male   Robert Redford    CHUD             James
1        Leading Female Miley Cyrus       Dark Horizons    James
1        Villain        Hugh Grant        CHUD             Sarah
2        Director       Mel Gibson        Yahoo            Cameron
2        Leading Male   John Lambert      Yahoo            Erica
...
于 2009-07-02T17:59:43.140 回答
1

有趣的场景。您可以通过将您的实体视为一流的对象来绕过 EAV 贫民区;让我们称它们为事实。在这种情况下,你是非常正交的,因为每部电影都有完全相同的四个事实,这会有所帮助。您的 EAV 表可以是您的原始/正确表,然后您可以有一个外部进程来挖掘该表并将数据复制到正确规范化的形式(即您的第一个表)。通过这种方式,您可以获得所需的数据及其元数据,并且您可以轻松地查询电影信息,精确到您的挖掘过程的运行频率。

我认为您肯定需要一些“数据库外”的力量来确保数据保持有效,因为似乎没有任何数据库内方法可以维护常规表和 EAV 表的完整性。我想通过一系列复杂的触发器,您几乎可以完成任何事情,但是“解决”您的问题的人工管理员可能更容易处理。

于 2009-06-30T21:05:14.117 回答
1

看到您只有两个源数据字段(来源和记者),我会推荐一个像这样的元数据表:

Movie    DirectorSource  DirectorJournalist  LeadingMaleSource  LeadingMaleJournalist ...
---------------------------------------------------------------------------------------
The Tick   Yahoo           Cameron           ...                ...

这会将不太重要的源数据保留在主表之外,但查询不会变得复杂,并且您的代码将更具可读性。

我只会建议EAV如果...

  • 您有超过 3 个源元数据字段
  • 需要能够轻松添加或更改电影字段。(像“恶棍”到“主要恶棍”这样的变化每天都会进行几次)
于 2009-07-01T00:23:05.433 回答
1

这是另一个想法...随意在其中打孔:)

Table: Movie
Columns: MovieId|Movie|Director|LeadMale|LeadFemale|Villain

Table: MovieSource
Columns: MovieSourceId|MovieId|MovieRoleId|Source|Journalist

Table: MovieRole
Columns: MovieRoleId|MovieRole
Values: 1|Director, 2|LeadMale, 3|LeadFemale, 4|Villain

我在想的是电影表中的列可能是不同的类型(在您的示例中,它们都是字符串/varchars,但它们可能是也有来源的数字或日期信息)。

但是,源数据的列类型可能不会随电影数据的列类型而变化,因此您可以对源使用更多的 EAV 系统,而不会丢失数据的完整性。

MovieRole 表允许您显式枚举角色,以便您可以在源和电影表的给定单元之间创建可靠的链接。

-担

于 2009-06-30T21:31:43.983 回答
0

由于没有其他人真正对此进行破解,我将回答我自己的问题。我很确定类似 EAV 的表确实是唯一的方法。要在每一列上存储元数据(在这种情况下,关于来源和记者),您实际上是将每一列视为一个实体,这是 EAV 所允许的。

可以走其他路线,例如为每个原始列添加第二列和第三列来存储数据,但这肯定违反了一些基本的规范化规则,并且可能只会在以后给您带来痛苦。

于 2009-06-30T14:34:12.467 回答
0

对于 SO,我的回答似乎有点过于哲学化。忍受我。

我认为“来源”列不是主题数据,而是元数据。这实际上是关于我们如何了解其他一些数据的数据。这使它成为关于数据的数据,这就是元数据。

EAV 造成问题的原因之一是它将数据和元数据混合在一行中。有时我自己故意这样做,作为实现我想要达到的结果的中间步骤。但我从未尝试在可交付成果中混合数据和元数据。

我知道为什么我从未这样做过,但我无法简明扼要地解释它。

于 2009-06-26T20:42:43.123 回答
0

嗯....我没用过这个,所以我不是根据经验说话(即,如果它不起作用,请不要怪我),但从表面上看,您似乎可以存储“常见”数据您知道将始终像在普通表中一样存在,并且“元数据”可能会更改为 XML。那么问题是如何很好地查询它,我认为您可以按照HERE的描述进行操作。

于 2009-06-30T20:30:30.550 回答
0

另一种要考虑的方法是类表继承。Bill Karwin 在这个 SO 答案中对 EAV 选项进行了很好的评论,并提供了很多好的上下文。

于 2009-07-01T00:37:56.743 回答
0

我会根据我需要编码的内容做出决定。

如果 src/journo 只是附加信息,我会继续阅读更多专栏。但如果我知道我最终要构建复杂的 src/journo 查询,我会选择 EAV,因为在元表中搜索记者的参考资料比进入LeadingFemaleJournalistVillainJournalist等更容易。

就个人而言 - 我倾向于将 src/journo 元数据转储到另一个 EAV 样式的表中,但使用 FK 来定义属性定义表。拥有自由格式的属性文本字段是灾难的秘诀 - 始终通过约束来控制您的属性。如果需要,可以实施触发器以提高参照完整性。

对我来说,它归结为观点。您是否认为消息来源和记者本身就是关系问题,或者他们只是补充电影的额外数据?下一个改进级别是为MovieDataSourceMovieDataJournalist创建不同的表,这可以让您将 FK 映射到定义有效SourcesJournalists的表(然后可以充实关于这些 Sources/Journalists 的更多信息)。您将在这里所做的是在Movie实体和Source(以及Journalist)实体之间建立多对多关系。

于 2009-07-02T08:04:28.923 回答