2

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

需要存储约 10^5 个实体 1000 天。最好的方法是什么?

我正在考虑2个选项:

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

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

性能优先于存储。

欢迎所有建议和意见。

4

6 回答 6

3

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

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

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

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

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

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

于 2011-05-18T16:27:57.503 回答
1

取决于实体是否更经常免费,仅存储实体是否免费的日期。

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

于 2011-05-17T13:00:35.517 回答
0

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

于 2011-05-17T13:04:42.483 回答
0

在此处输入图像描述

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

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注意:确保只在表中存储状态更改。

于 2011-05-17T14:45:06.887 回答
0

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

  • FactEntity (FK kStatus, kDate)
  • 昏暗状态(PK kStatus)
  • DimDate (PK kDate)

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

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

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

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

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

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

于 2011-05-17T13:58:32.570 回答