【问题标题】:Database schema design pattern for data that merges合并数据的数据库模式设计模式
【发布时间】:2015-08-07 11:26:25
【问题描述】:

我们有一个相当陈旧的数据库,其中包含大量个人以及他们已完成的许多成就。历史上几乎没有采取任何措施来阻止重复的个人数据,因此我们最终陷入了数据非常脏的情况。可以在here 找到一个大大简化的版本。

我们现在正在重新设计架构和用户界面。我们将为用户提供一个工具来将他们的个人合并在一起。在提供的示例中,Dave 和 David 显然是同一个人,总共取得了 4 项成就。

鉴于用户会犯错误并且涉及的表比示例中的多得多,因此我正在寻找一种便于数据合并的架构设计,尤其是在(何时!)用户不可避免地会犯错误。

某种类型的链接列表似乎是一种解决方案,但对于这个用例来说并不完全有效。是否有任何其他概念可能适用于这种情况?任何可能合适的特定设计模式?

编辑:由于今天 SQLFiddle 相当不稳定,这里是 sqlfiddle 上的 create/insert/select sql:

CREATE TABLE individual
    (`individual_id` int, `forename` varchar(50), `surname` varchar(50))
;

CREATE TABLE achievement
    (`achievement_id` int, `name` varchar(50), `description` varchar(50))
;

CREATE TABLE individual_achievement
    (`individual_id` int,`achievement_id` int)
;

INSERT INTO individual
    (`individual_id`, `forename`, `surname`)
VALUES
    (1, 'Dave', 'Deane'),
    (2, 'David', 'Deane')
;

INSERT INTO achievement
    (`achievement_id`, `name`, `description`)
VALUES
    (1, 'unit_1', 'Unit 1'),
    (2, 'unit_2', 'Unit 2'),
    (3, 'unit_3', 'Unit 3'),
    (4, 'unit_4', 'Unit 4')
;

INSERT INTO individual_achievement
    (`individual_id`,`achievement_id`)
VALUES
    (1, 1),
    (1, 3),
    (2, 2),
    (2, 4)
;

select * from individual i
join individual_achievement ai using (individual_id)
join achievement a using (achievement_id)

编辑 2: 刚刚找到这个 very similar question,希望 4 年后也有其他解决方案。

【问题讨论】:

  • 看不到 SQLFiddle,但你为什么不自动合并成就而不是让用户来做呢?
  • 合理的问题@plalx。我们没有足够的可用信息来可靠地检测重复项。在很多情况下,我们只有姓氏和姓氏,这绝对是不够的。在许多情况下,我们确实有出生日期,但即便如此,也有可能是错误的。我们的计划是向用户突出显示我们认为可能重复的那些,但只有用户才能确定他们的个人是否重复。

标签: design-patterns database-design merge database-schema


【解决方案1】:

这是您可以使用的一种策略。

首先,创建一个新表,现在将其称为“Individual_v2”,其列与原始表 Individual 完全相同。 (理想情况下,您最终将用此表替换个人;实际上,人们仍可能将数据输入到个人中,您必须通过将数据移动或合并到个人_v2 中来“清理”数据。)使用指向成就的链接配置此表。 (现在,我假设成就是干净的。)

然后,像这样创建一个“映射”表:

IndividualMapping

OldIndividual_Id
NewIndividual_Id
CreatedAt
CreatedBy
ApprovedAt  --  Nullable!
ApprovedBy  --  Nullable!

“创建”列用于确定映射的创建时间和创建者(或内容)。

“已批准”列用于确定数据是否已迁移到新表。

对于每个“旧”项目,您可以确定它在“新”表中的映射位置;如果它没有映射到现有项目,请在新表中为其创建一个。

然后,在映射表中添加一个条目。如果创建了新项目,则将其标记为已批准;如果信心高,将其标记为已批准;否则,让它“未经批准”并等待审核。在适当的时候,审阅者会检查并批准映射,将映射更改为不同的现有新项目,或创建另一个新项目并映射到它。

一旦完成,“真正的”工作就会针对新表完成。旧表和映射表可用于识别新数据的来源,并在必要时撤消/更改映射。

这里有很多未解决的实施和支持问题,总体上看起来很尴尬。从长远来看,一旦您解决了重复数据的问题,您就可以删除旧的(和映射)表,但在此之前,您将拥有一个繁琐的系统。


附录

我在这里只是在谈论事情,没有进行详尽的分析。我认为您所描述的系统将是繁琐且概念上复杂的工作,即使表格相对简单,最终细节超出 SO 问题的范围。同样,很大程度上取决于系统的总体目标和目标及其重新设计。我将在这里做一些假设:

  • “现有”系统将保持不变

  • 如此输入的个人(及其奖励)必须立即提供,一如既往。

  • 将继续输入重复项;如果、何时并在可行的情况下,将它们与预先存在的条目“合并”

这样做,系统将按如下方式工作:

  • Individuals_v2 和Achievement 之间有一个单独的关系表(目前是Individual_Achievement_v2,尽管必须有更好的名称)。

  • “v2”表中的数据正确、良好且正确。 “v1”表是暂存、历史、日志数据。

  • 准备初始版本,其中 v1 表中的 所有 条目在 v2 表中配置。如果可以在此步骤中合并行,那就更好了。一切都记录在“地图”表中,以便在必要时可以干净地返回和重做。

  • 从此版本开始,新数据将输入到 v1 表中,同时/立即输入到 v2 表中。如果可以映射到现有项目,请执行此操作,否则在 v2 表中创建一个新条目。始终在“地图”表中记录活动。

  • 接下来,所有“实时”查询都会命中 v2 表。 v1 表(再次)是历史、日志、审计跟踪。一旦填充,它们就永远不会被修改,而 v2 表(包括映射表)可以并且将会被修改。

  • 根据业务确定,定期审查/检查数据,以查找和修复随着时间的推移出现的重复条目以及“无效重复”(不正确的映射)。这是您在映射和 v1 表中跟踪的回滚/重做工作的时间。

您可能需要一些额外的日志记录表,以跟踪诸如“通过 xx/xx/xxxx 输入的所有数据都是有效的,此后输入的数据必须经过审查”之类的事情。我敢肯定还会出现其他问题和微妙之处——它们总是会出现……

【讨论】:

  • 我明白你的想法了,我想这里最大的假设是用户永远不会再创建一个重复的个人。如果他们这样做了,那么我们将不得不使用 individual_v3 重新开始这个过程。对于 individual_assessment 表,您是为 individual_v2_id 添加一列还是简单地更新 individual_id 列? (然后也为此创建另一个映射表?)
  • 在答案的“附录”部分对此进行了回复。
  • 感谢 Phillip,现在这是一个更全面的解决方案。优点:可以回滚单个合并而不是时间点(命令模式)。缺点:对于需要合并的每个表,都需要 2 个额外的表。
  • 是的,它的多对多关系使它变得棘手。没有它,应该可以将版本控制塞进一个表中。
猜你喜欢
  • 1970-01-01
  • 2010-10-17
  • 1970-01-01
  • 1970-01-01
  • 2021-11-08
  • 1970-01-01
  • 1970-01-01
  • 2014-03-06
  • 2011-07-25
相关资源
最近更新 更多