将以下时态数据存储在DB中的最佳方法

时间:2011-05-17 12:54:07

标签: database performance database-design temporal

想象一下,我们有一组实体,每个实体都有自己的状态:自由,忙碌或破碎。状态指定为一天,例如,今天在2011-05-17,实体E1是免费的,明天在2011-05-18它正忙。

需要存储~10 ^ 5个实体1000天。这是最好的方法吗?

我正在考虑两个选项:

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

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

性能优先于存储。

欢迎所有建议和意见。

6 个答案:

答案 0 :(得分:3)

最好的方法是先尝试更简单,更灵活的选项(也就是说,将每一天存储在自己的行中),如果性能不理想,只设计一种复杂的替代方法。避免过早优化。

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

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

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

答案 1 :(得分: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个演出。

将表安装在SSD上,然后设置为。

答案 2 :(得分:1)

取决于实体是否更经常免费或不存储实体是否空闲的日期。

假设您存储实体不是免费的日期,那么搜索就是开始日期< = date和end_date> =日期和任何匹配的行,这意味着该实体在该期间不是免费的

答案 3 :(得分:0)

听起来你可能走在正确的轨道上,我建议因为大量的记录和对性能的强调,你要尽可能地将模式化为非规范化。您需要更少的连接来确定空闲或繁忙的实体。

答案 4 :(得分:0)

我会广泛选择带有三个表格的Kimball Star Schema(http://en.wikipedia.org/wiki/Star_schema)类型结构(最初)

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

这可以非常简单地加载(Dims首先跟随Fact),并且也非常简单地查询。可以通过适当的索引来优化性能。

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

可以合理地添加其他尺寸,例如DimEntity可以提供更丰富的信息,这些信息可以提供有趣的分类信息来切割/切割您的实体。

DimDate通常通过添加DayNo,MonthNo,YearNo,DayOfWeek,WeekendFlag,WeekdayFlag,PublicHolidayFlag来丰富。这些可以进行一些非常有趣的分析。

正如@Elad所问,如果你添加了基于时间的信息,那会是什么啊,这也可以通过每小时或每分钟有一条记录的DimTime维度来实现。

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

答案 5 :(得分:0)

enter image description here

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

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表中存储状态更改。