为了稳定性或性能而进行反规范化?

8

我开始了一个新项目,他们有一个非常规范的数据库。所有可以作为查找的内容都存储为查找表的外键。这是规范的,但是我最简单的查询需要进行5个表连接。

    from va in VehicleActions
    join vat in VehicleActionTypes on va.VehicleActionTypeId equals vat.VehicleActionTypeId
    join ai in ActivityInvolvements on va.VehicleActionId equals ai.VehicleActionId
    join a in Agencies on va.AgencyId equals a.AgencyId
    join vd in VehicleDescriptions on ai.VehicleDescriptionId equals vd.VehicleDescriptionId
    join s in States on vd.LicensePlateStateId equals s.StateId
    where va.CreatedDate > DateTime.Now.AddHours(-DateTime.Now.Hour)
    select new {va.VehicleActionId,a.AgencyCode,vat.Description,vat.Code,
vd.LicensePlateNumber,LPNState = s.Code,va.LatestDateTime,va.CreatedDate}

我建议我们对某些内容进行去规范化,例如州代码。我认为在我的有生之年内州代码不会发生变化,3个字母的机构代码也是如此。这些代码由各机构分配,永远不会改变。
当我向数据库管理员提出州代码问题和5个表连接的问题时,得到的回答是“我们已经规范化了”,“连接很快”。
是否有令人信服的理由去规范化?如果没有其他原因,我会这样做以保持清晰明了。
T-SQL中的相同查询:
    SELECT VehicleAction.VehicleActionID
      , Agency.AgencyCode AS ActionAgency
      , VehicleActionType.Description
      , VehicleDescription.LicensePlateNumber
      , State.Code AS LPNState
      , VehicleAction.LatestDateTime AS ActionLatestDateTime
      , VehicleAction.CreatedDate
FROM VehicleAction INNER JOIN
     VehicleActionType ON VehicleAction.VehicleActionTypeId = VehicleActionType.VehicleActionTypeId INNER JOIN
     ActivityInvolvement ON VehicleAction.VehicleActionId = ActivityInvolvement.VehicleActionId INNER JOIN
     Agency ON VehicleAction.AgencyId = Agency.AgencyId INNER JOIN
     VehicleDescription ON ActivityInvolvement.VehicleDescriptionId = VehicleDescription.VehicleDescriptionId INNER JOIN
     State ON VehicleDescription.LicensePlateStateId = State.StateId
Where VehicleAction.CreatedDate >= floor(cast(getdate() as float))
7个回答

6

有时出于性能(和理智)原因可能需要进行一些反规范化。如果没有看到你的所有表/需求等,很难确定。

但是为什么不构建一些方便的视图(执行一些连接),然后使用这些视图编写更简单的查询呢?


尽可能地,我们应该将小型、简单、可重用的函数的思想应用于我们编写的所有代码。对于这类事情,我从表值函数和视图中获得了很多收益。而且,作为额外的好处,报告也变得更加容易了。 - overslacked

6

小心想要将事物塑造成您当前的用语方式。现在,陌生的代码似乎很笨重,妨碍您的理解。随着时间的推移,您可能会逐渐适应它。

如果当前(或已知的未来)需求,如性能没有得到满足,那么这是一个完全不同的问题。但请记住,任何东西都可以进行性能调优,目标不是让事情尽可能快,而是让它们足够快。


1
+1 是指出开发人员随着时间的推移而成长。我认为在这种情况下,最好学会处理超标准化的数据并进行调整,而不是将数据调整到我们熟悉的状态。 - David

6
我不知道我是否应该把你想要做的事情称为去规范化 -- 它看起来更像是你只想用自然外键 (州简称, 机构代码) 来替换人工外键 (StateId, AgencyId)。使用 varchar 字段代替整数字段会减慢连接/查询性能,但如果您大多数时间甚至不需要连接表格,因为自然的 FK 是您想要的,那么这并不是什么大问题;而且如果您的数据库非常大/负载高,它才会有所察觉。
但是,djna 是正确的,你需要完全理解当前和未来的需求,然后再做出这样的改变。您确定三字母机构代码甚至在五年后都永远不会更改吗?真的、真的确定吗?

1
我曾经是自然外键优雅、逻辑清晰的忠实拥趸,但它们不值得为之付出持续的维护麻烦。因此,我创建了优雅的工具来管理人工键,这样每个人都能及时回家吃晚饭。 - overslacked

3

这篇先前的帖子处理了一个与你遇到的类似问题。希望它对你有帮助。

处理“超范式化”的数据

我个人对规范化的看法是尽可能地规范化,但仅在性能方面进行反规范化。甚至为了性能而进行的反规范化也是应该避免的。在反规范化之前,我会先分析性能、设置正确的索引等方案。

理智... 那被高估了。特别是在我们这个行业。


+1 给“Sanity”这个说法。偶尔引用你的话可以吗?;-) - sleske

3

那么,性能如何?如果性能还可以,只需将这五个表连接成一个视图,并在需要数据时从该视图中进行SELECT查询。

州缩写是我认为有意义的键值之一。对于非常简单的查找表,行数有限且我完全控制数据(即不是从外部来源填充的),我有时会创建有意义的四到五个字符的键值,以便在某些查询中代理完全描述性的查找值。


3

创建视图(或内联表值函数以获取参数化)。无论如何,我通常将所有代码都放入存储过程中(一些代码是生成的),无论它们是否使用视图,这就是全部,你几乎只需编写一次连接。


2

如果没有针对三字代码更改的计划,那么支持“规范化”的论点并不令人信服。如果代码确实发生了变化,你的人工键方案如何比使用代码作为键更好地解决这个问题?除非你已经实现了完全的时间模式(这是非常困难的,并且不建议按照你的示例进行),否则我不清楚你的规范化如何使你受益。但是,如果你与多个来源和标准的机构合作,并且可能存在冲突的代码名称,或者如果“州”最终可能意味着一个州、省、部门、坎通或埃斯塔多的两个字母代码,那就另当别论了。那么你需要自己的键,或者你需要一个包含比该代码更多信息的双列键。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接