【问题标题】:Denormalising a fully normalised table对完全规范化的表进行非规范化
【发布时间】:2013-03-08 03:19:10
【问题描述】:

谁能建议如何去规范化一个完全规范化的表?尽管谷歌搜索,我还是找不到太多关于它的信息。

每当在 TableA 中更新记录(虽然没有插入,但现在不用担心)时,都会将记录插入到 HistoryOfTableA 中,其中包含已更改字段的值以及执行时相关的时间戳。

例如。

TableA 字段:

TableA_Id, FieldA, FieldB, FieldC, FieldD.... etc

HistoryOfTableA 记录:

HistID, TableA_Id, FieldChanged, OldValue, NewValue, DateCreated
1, 1, 'FieldA', 1, 2, <2013-03-18 12:20:00>
2, 1, 'FieldB', A, B, <2013-03-18 12:20:00>
3, 1, 'FieldC', A, B, <2013-03-18 12:20:00>

情况是我希望创建一些用于报告目的的 SQL。因此,我希望能够指定一个时间点,并能够将这些主机表条目汇总在一起,并计算出当时 TableA 中该记录的状态。

我在想我可以根据历史表创建表,并在创建日期和表 A 中加入 Id

例如。

select HistA.NewValue, 
       HistB.NewValue, 
       .... 
from FieldA_HistoryOfTableA HistA 
     inner join FieldB_HistoryOfTableA HistB on HistA.DateCreated = HistB.DateCreated
       and HistA.TableA_Id = HistB.TableA_Id 
     inner join ... etc
where HistA.FieldChanged = 'FieldA'
and HistB.FieldChanged = 'FieldB'
and .... etc...

但我不认为这会给我想要的所有东西,而且我可能无法纯粹在 SQL 中做到这一点。顺便说一句,TableA 中有 20 个字段,因此尝试加入 20 个表可能并不明智。

【问题讨论】:

  • 您使用的是哪个 DBMS?

标签: sql normalization multiple-tables denormalization


【解决方案1】:

你的问题不是表格被规范化,而是它没有。

一个表中的字段标识的数据由另一个表中的数据标识,查询起来非常复杂。

完全规范化的版本还将来自TableA 的字段存储在单独的表中:

TableA
------------
TableA_Id

TableAFields
-------------
TableA_Id
FieldId
Value

Field
---------
FieldId
FieldName

HistoryOfTableA
----------------
TableA_Id
FieldId
OldValue
ChangedDate

现在您可以加入历史表中的字段。这会有点棘手,但至少不会是一个有 20 个连接的查询。

【讨论】:

  • 嗯,是的,我同意你的观点,不幸的是,我无权更改架构。
  • @atamata:我明白了,我认为你可以这样做,因为这就是你在非规范化表格时所做的事情。然后你就走上了复杂的道路……至少你知道是什么让它变得复杂。
【解决方案2】:

如果想要历史表中的最新记录,可以使用以下 SQL:

select ht.*
from (select ht.*,
             ROW_NUMBER() over (partition by FieldChanged, TableA_Id order by DateCreated desc) as seqnum
      from HistoryOfTableA ht
      where datecreated <= YOURDATEHERE
     ) ht
where seqnum = 1

如果您希望将其作为单个记录,您应该进行透视或聚合。这是后一种方法:

select ht.TableA_id,
       max(case when FieldName = 'FieldA' then NewValue end) as FieldA,
       . . .
from (select ht.*,
             ROW_NUMBER() over (partition by FieldChanged, TableA_Id order by DateCreated desc) as seqnum
      from HistoryOfTableA ht
      where datecreated <= YOURDATEHERE
     ) ht
where seqnum = 1
group by TableA_id

【讨论】:

  • 谢谢,这很有帮助,我实际上会将它用于我的其他一些报告。
【解决方案3】:

您可以尝试使用 TableA 中的 XML 列来记录 TableA 中每一行的历史记录。即每一行都有一个 XML 类型的列,并记录该行的历史记录。然后您可以将行拉入您的应用程序(如果行数很大,则以批处理方式),并找出 TableA 中每条记录的历史记录

【讨论】:

  • 这听起来是个坏主意。 XML 在列中存储历史记录?您认为更新会是什么样子?
  • 嗨丹尼尔,每个更新存储过程都可以通过以下方式记录该列中的历史记录:1)在更新发生之前拍摄记录的快照,或者 2)只记录更改的字段,因为我们在更新时知道字段会改变。您能否详细说明为什么这是一个坏主意?
  • 对于每次更新,您必须 (1) 解析 XML,(2) 附加新的历史记录和 (3) 持久化 XML。此外,现在不再可以通过 SQL 访问该历史记录。
  • 同意解析部分是开销。但是有一些合法的设计模式可以读取然后更新数据。第 2 点和第 3 点自然遵循阅读和坚持的模式。同样,有允许 XML 查询的 DBMS 技术。
猜你喜欢
  • 1970-01-01
  • 2016-11-12
  • 2013-08-21
  • 2016-05-13
  • 2018-06-06
  • 1970-01-01
  • 2012-12-31
  • 2018-12-12
  • 2021-06-13
相关资源
最近更新 更多