【问题标题】:The best way to store the following temporal data in DB在 DB 中存储以下时态数据的最佳方式
【发布时间】:2011-05-17 12:54:07
【问题描述】:

假设我们有一组实体,每个实体都有自己的状态:空闲、忙碌或损坏。状态指定为一天,例如,今天 2011 年 5 月 17 日,实体 E1 空闲,明天 2011 年 5 月 18 日它很忙。

需要存储约 10^5 个实体 1000 天。哪种方法最好?

我正在考虑 2 个选项:

  • 将每一天表示为字符“0”、“1”或“2”,并为每个实体存储一个 1000 个字符的字符串
  • 将每一天的实体状态存储在一行中,即一个实体有 1000 行

对此类数据最重要的查询是:给定开始日期和结束日期,确定哪些实体是免费的。

性能优先于存储。

欢迎所有建议和cmets。

【问题讨论】:

  • 如果一个实体在第 10 天和第 12 天是免费的,但不是在第 11 天——是否应该将其视为“在第 10 天和第 12 天之间免费”?
  • 它必须明确定义,即如果它在第 10 天和第 12 天是空闲的,那么我们对第 11 天一无所知。但是,如果存储时间段,则可以这样定义:[10, 12]是免费的。但我发现这种方式更复杂。

标签: database performance database-design temporal


【解决方案1】:

最好的方法是先尝试更简单、更灵活的选项(即,将每一天存储在自己的行中),然后仅在性能不令人满意时设计一种复杂的替代方法。避免过早优化。

10^8 行对于当今商品服务器上的普通数据库来说并不是什么大问题。在日期上放置一个索引,我敢打赌范围查询(“给定开始日期和结束日期......”)会正常工作。

我声称这比存储 1000 个字符的字符串更简单、更灵活的原因是:

  • 您必须在代码中进行处理,而且该代码不像查询包含日期和状态的数据库记录的代码那样简单易懂。
  • 根据数据库引擎,1000 个字符串可能是存储在记录之外的 blob。这降低了他们的效率。
  • 如果您突然需要 2,000 天而不是 1,000 天会怎样?开始更新所有行和处理它们的代码?这比仅仅更改查询要多得多。
  • 如果您接下来被要求为每个每日记录存储一些额外信息,或者需要更改粒度(例如从几天改为几小时),会发生什么情况?

【讨论】:

  • 同意 - 在索引列上执行范围查询比针对计算数组一个接一个地屏蔽 100k 个实体要快得多。在存储方面不太紧凑,但速度更快。我假设这个 EntityStatus 表将只包含一个实体 ID、一个日期和一个状态(空闲、损坏等)。
【解决方案2】:

创建一个表来保存您的数据。使用 ID、日期、实体名称和八个布尔字段创建表。 SQL Server 2008 给了我下面的表格代码:

CREATE TABLE [dbo].[EntityAvailability](
[EA_Id] [int] IDENTITY(1,1) NOT NULL,
[EA_Date] [date] NOT NULL,
[EA_Entity] [nchar](10) NOT NULL,
[EA_IsAvailable] [bit] NOT NULL,
[EA_IsUnAvailable] [bit] NOT NULL,
[EA_IsBroken] [bit] NOT NULL,
[EA_IsLost] [bit] NOT NULL,
[EA_IsSpare1] [bit] NOT NULL,
[EA_IsSpare2] [bit] NOT NULL,
[EA_IsSpare3] [bit] NOT NULL,
[EA_IsActive] [bit] NOT NULL,
 CONSTRAINT [IX_EntityAvailability_Id] UNIQUE NONCLUSTERED 
(
    [EA_Id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]
END
GO

IF NOT EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[EntityAvailability]') AND name = N'IXC_EntityAvailability_Date')
CREATE CLUSTERED INDEX [IXC_EntityAvailability_Date] ON [dbo].[EntityAvailability] 
(
    [EA_Date] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
GO

日期聚集索引最适合您的范围搜索。绝不允许没有日期范围的搜索,并且不需要除聚集索引之外的任何索引。布尔字段允许仅使用单个字节的八种情况。此表的行大小为 35 字节。 230 行将适合一页。您说您需要存储 10^5 个实体 1000 天,即 1 亿。一亿行将占用 434,782 个 8K 页面或大约 3 gig。

将桌子安装在 SSD 上,您就可以开始使用了。

【讨论】:

    【解决方案3】:

    取决于实体是否更频繁地空闲,只存储实体空闲与否的日期。

    假设您在实体不空闲时存储日期,则搜索是开始日期 = 日期以及任何匹配的行,这意味着实体在该期间不空闲

    【讨论】:

    • 好的,一个实体未来可能有两个以上的状态,例如,空闲、忙碌和破碎
    • 如果只有 3 则为繁忙和损坏的每个创建一个表 - 否则在日期表中添加标志以表示繁忙、损坏等。
    【解决方案4】:

    听起来您可能走在正确的轨道上,由于记录的绝对数量和对性能的强调,您应该尽可能地保持架构非规范化。确定空闲或忙碌实体所需的连接越少越好。

    【讨论】:

    • 在这种情况下,连接可能更多地与使用代理 ID 号有关,而不是与规范化有关。 “不要为州使用代理 ID 号 - 使用 CHAR(1) 和 'F'、'B' 和 'X'”是个好建议。
    【解决方案5】:

    我会广泛地使用具有三个表的 Kimball Star Schema (http://en.wikipedia.org/wiki/Star_schema) 类型结构(最初)

    • FactEntity (FK kStatus, kDate)
    • DimStatus (PK kStatus)
    • DimDate (PK kDate)

    这可以很简单地加载(Dims 先跟 Fact(s)),查询也很简单。可以通过适当的索引来优化性能。

    这种设计的一大优点是可扩展性很强;如果你想增加日期范围,或者增加有效状态的数量,扩展是微不足道的。

    可以明智地添加其他维度,例如DimEntity 可能具有更丰富的信息,可以提供可能对您的实体进行切片/切块感兴趣的分类信息。

    通常通过添加 DayNo、MonthNo、YearNo、DayOfWeek、WeekendFlag、WeekdayFlag、PublicHolidayFlag 来丰富 DimDate。这些允许执行一些非常有趣的分析。

    正如@Elad 所问的那样,如果您添加基于时间的信息会发生什么,那么这也可以通过每小时或每分钟具有一条记录的 DimTime 维度来提供信息。

    抱歉我的命名,因为我对您的数据没有很好的理解。如果有更多时间,我可以想出一些更好的!

    【讨论】:

      【解决方案6】:

      要在约会时获得免费实体,您可以尝试:

      select
            e.EntityName
          , s.StateName
          , x.ValidFrom
      from EntityState as x
      join Entity      as e on e.EntityId = x.EntityId
      join State       as s on s.StateID  = x.StateID
      where StateName = 'free'
        and x.ValidFrom = ( select max(z.ValidFrom)
                            from EntityState as z
                            where z.EntityID   = x.EntityID
                              and z.ValidFrom <= your_date_here )
      ;
      

      注意:确保您只将状态更改存储在 EntityState 表中。

      【讨论】:

        猜你喜欢
        • 2018-12-11
        • 2013-01-03
        • 2018-12-01
        • 2016-07-19
        • 2020-07-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多