0

我正在努力设计一个代表建筑物及其租户开放时间的数据库模式。以下是详细信息/要求:

建筑物:

  • 一般来说,每个建筑物的正常营业时间取决于季节(夏季、冬季等)
  • 每栋楼都有不同的营业时间。
  • 许多节假日、活动等将覆盖正常营业时间

租户:

  • 租户在建筑物内经营,因此应受到建筑时间的限制。
  • 正常时间也取决于季节,但与其建筑物的季节性时间不同。
  • 每个租户有不同的时间

理想情况下,我希望能够查询以下内容:

  • 建筑物或租户现在是否开放
  • 今天的建筑时间是几点
  • 未来 X 天的建筑时间是多少

到目前为止,我的(未完成的)工作是这三个表,但我仍然无法创建一个有效的解决方案。

[Season]
id
building_id
title
start_date
end_date

[Schedule]
id
season_id
day_of_week (0-6)
open_time
close_time

[Override]
id
schedule_id
date
is_closed
is_holiday

感谢大家的时间和意见。所有答案都在开发/完善解决方案。Catcall 存储单个日期的想法对我们来说是最容易开发模型和管理/管理界面的。

4

3 回答 3

1

你的桌子设计有了一个很好的开始。

建造时间(到目前为止你的模型)

[Override]可以调整您的表以简化数据维护和查询。

假设您的业务规则是覆盖总是针对(一整天)一天,那么您希望在季节级别而不是计划级别进行覆盖。这使您不必担心一周中的哪一天会发生覆盖。通过在覆盖表中包含 start_date 和 end_date 列,您也可以更轻松地进行概括。此外,您的标志 ( is_closed, is_holiday) 可以简化为单个枚举列 ( closed_reason)。这将允许您在将来添加新的原因,而无需更改您的架构和查询。

租户时间(下一步)

您的租户计划模型应该与您的建筑时间相似。根据您预计租户小时数与建筑小时数不同的频率,您可以在租户表上包含一个标志来指示uses_building_schedule。这将表明没有租户计划条目。

对于时间表与他们占用的建筑物不同的租户,只需使用相同的季节/时间表/覆盖结构。

由于您有一条规定租户工时必须受建筑工时限制的规则,因此您需要添加程序代码来强制执行此规则。我会在查询时进行,而不是在数据维护时进行。换句话说,当查询租户的营业时间时,我会查看他们是否有营业时间,然后将这些营业时间限制为建筑营业时间。

于 2012-05-19T12:03:43.263 回答
1

我不确定它在多大程度上符合您的要求,但让我提供一个想法:

在此处输入图像描述

该模型具有以下特点:

  • 建筑物或租户有时间表(可能会或可能不会与其他建筑物/租户共享)。
  • 时间表是一组间隔。
  • 每个间隔在计划中都有一个优先级:具有较高优先级的间隔“覆盖”具有较低优先级的间隔。看看 SCHEDULE_ITEM 是如何受到约束的,因此没有时间间隔可以两次属于同一个计划,并且相对于属于同一计划的其他时间间隔不能具有不明确的优先级(唯一约束U1)。
  • 可以在时间表之间共享间隔,例如,您可以设置涵盖年度事件的单个间隔,然后在多个时间表之间共享。如果事件发生变化,您只需在一处进行更新。

这个模型的问题在于它的处理量很大,并且 DBMS 无法帮助您避免数据中出现一些无意义的情况(您需要在应用程序级别执行此操作)。另一方面,它非常灵活和强大。

在给定日期查找建筑物或租户的小时数并非易事:您需要按照优先级的顺序将时间表下的所有时间间隔相交。要找到给定租户的小时数,您需要先找到她的建筑物的小时数,然后将它们相交。

例子

一个时间表...

  • 全年上午 9 点至下午 5 点,
  • 除了从 5 月到 10 月的上午 8 点到下午 6 点,
  • 周六上午 9 点至下午 1 点除外,
  • 周日没有时间
  • 1 月 1 日、7 月 4 日和 12 月 25 日不营业

...可以这样表示:

SCHEDULE_ID     PRIORITY    MONTH_START     MONTH_END       DAY_OF_MONTH_START      DAY_OF_MONTH_END        DAY_OF_WEEK_START       DAY_OF_WEEK_END     HOUR_START      HOUR_END
1               1           NULL            NULL            NULL                    NULL                    NULL                    NULL                9 AM            5 PM
1               2           5               10              NULL                    NULL                    NULL                    NULL                8 AM            6 PM
1               3           NULL            NULL            NULL                    NULL                    6                       6                   9 AM            1 PM
1               4           NULL            NULL            NULL                    NULL                    7                       7                   NULL            NULL
1               5           1               1               1                       1                       NULL                    NULL                NULL            NULL
1               6           7               7               4                       4                       NULL                    NULL                NULL            NULL
1               7           12              12              25                      25                      NULL                    NULL                NULL            NULL
于 2012-05-19T14:08:10.657 回答
1

让我们以不同的方式看待这个问题。建筑物和租户的营业时间并没有本质上的不同。也就是说,价值可能会有所不同,但建筑物是否开放的想法与租户是否开放并没有本质上的不同。我创建了一个单独的模式“小时”来实现这个想法。

-- Essentially a supertype, but I couldn't think of a good noun to 
-- describe it.  So I named the table "x".  It holds all the attributes 
-- common to both buildings and tenants. (That is, not very much.)
--
create table hours.x (
  x_id integer primary key,
  x_name varchar(35) not null,
  x_type char(1) check (x_type in ('b','t')),
  unique (x_id, x_type)
);

insert into hours.x values 
(1, 'First building', 'b'),
(2, 'Second building', 'b'),
(3, 'First tenant', 't'),
(4, 'Second tenant', 't'),
(5, 'Third tenant', 't');

create table hours.buildings (
  bldg_id integer primary key,
  x_type char(1) not null default 'b' check (x_type = 'b'),
  foreign key (bldg_id, x_type) references hours.x (x_id, x_type),
  other_columns char(1) not null default 'x'
);

insert into hours.buildings (bldg_id) values 
(1), (2);

create table hours.tenants (
  tenant_id integer primary key,
  x_type char(1) not null default 't' check (x_type = 't'),
  foreign key (tenant_id, x_type) references hours.x (x_id, x_type),
  bldg_id integer not null references hours.buildings (bldg_id),
  other_columns char(1) not null default 'x'
);

insert into hours.tenants (tenant_id, bldg_id) values
(3, 1), (4, 1), (5, 2);


-- Operating hours records a half open interval [opening_time, closing_time).
-- If a queried time matches the opening time, the building or tenant is open.
-- But if it matches the closing time, it's *not* open.  Examples later.
create table hours.op_hours (
  x_id integer not null references hours.x (x_id),
  opening_time timestamp not null,
  closing_time timestamp not null,
  check (opening_time < closing_time),
  check (
    -- Open and close on the same date,
    ( opening_time::date = closing_time::date ) or
    -- or close at midnight following the opening.
    ( 
      (opening_time::date + interval '1' day = closing_time::date) and 
      (cast(closing_time as time) = '00:00')
    )
  )
);

insert into hours.op_hours values
-- Bldg 1 is normally Mon-Sat, 9:00 to 5:00. Closed on Jan 1, holiday
(1, '2012-01-02 09:00', '2012-01-02 17:00'),
(1, '2012-01-03 09:00', '2012-01-03 17:00'),
(1, '2012-01-04 09:00', '2012-01-04 17:00'),
(1, '2012-01-05 09:00', '2012-01-05 17:00'),
(1, '2012-01-06 09:00', '2012-01-06 17:00'),
(1, '2012-01-07 09:00', '2012-01-07 17:00'),
-- Closed on Jan 8, a Sunday.
(1, '2012-01-09 09:00', '2012-01-09 17:00'),

-- Bldg 2 is normally Mon-Fri, 7:30 to midnight.
(2, '2012-01-02 07:30', '2012-01-03 00:00'),
(2, '2012-01-03 07:30', '2012-01-04 00:00'),
(2, '2012-01-04 07:30', '2012-01-05 00:00'),
(2, '2012-01-05 07:30', '2012-01-06 00:00'),
(2, '2012-01-06 07:30', '2012-01-07 00:00'),
-- Closed on Jan 7 and 8, weekend.
(2, '2012-01-09 07:30', '2012-01-10 00:00'),

-- "First" tenant is open 9:00 to noon and 1:00 to 4:00, Mon-Fri.
(3, '2012-01-02 09:00', '2012-01-02 12:00'),
(3, '2012-01-03 09:00', '2012-01-03 12:00'),
(3, '2012-01-04 09:00', '2012-01-04 12:00'),
(3, '2012-01-05 09:00', '2012-01-05 12:00'),
(3, '2012-01-06 09:00', '2012-01-06 12:00'),
(3, '2012-01-02 13:00', '2012-01-02 16:00'),
(3, '2012-01-03 13:00', '2012-01-03 16:00'),
(3, '2012-01-04 13:00', '2012-01-04 16:00'),
(3, '2012-01-05 13:00', '2012-01-05 16:00'),
(3, '2012-01-06 13:00', '2012-01-06 16:00'),

-- "Second" tenant is open when the building is open.
(4, '2012-01-02 09:00', '2012-01-02 17:00'),
(4, '2012-01-03 09:00', '2012-01-03 17:00'),
(4, '2012-01-04 09:00', '2012-01-04 17:00'),
(4, '2012-01-05 09:00', '2012-01-05 17:00'),
(4, '2012-01-06 09:00', '2012-01-06 17:00'),
(4, '2012-01-07 09:00', '2012-01-07 17:00'),
-- Closed on Jan 8, a Sunday.
(4, '2012-01-09 09:00', '2012-01-09 17:00'),

-- "Third" tenant is open Mon-Thu 7:30 to 9:30, Fri until midnight.
(5, '2012-01-02 07:30', '2012-01-02 21:30'),
(5, '2012-01-03 07:30', '2012-01-03 21:30'),
(5, '2012-01-04 07:30', '2012-01-04 21:30'),
(5, '2012-01-05 07:30', '2012-01-05 21:30'),
(5, '2012-01-06 07:30', '2012-01-07 00:00'),
-- Closed on Jan 7 and 8, weekend.
(5, '2012-01-09 07:30', '2012-01-09 21:30');

应该清楚的是,这种记录每个建筑物和每个租户每天的营业时间的结构将适应季节、假期、事件等的任何定义。可以使用相当简单的存储过程生成默认数据。而且所需的查询非常简单。(能够看到一些东西并看到它是正确的,这是很有价值的。)

-- Is building 1 open at 9:00 am on Jan 3? (Queries for tenants are essentially
-- identical. Returns the id number if it's open, but that could be massaged 
-- into an "is_open" derived column with Boolean values.)
select x_id
from hours.op_hours
where x_id = 1
  and opening_time <= '2012-01-03 09:00' 
  and '2012-01-03 09:00' < closing_time;

-- How about on Jan 1? (Returns an empty set if closed. See above.)
select x_id
from hours.op_hours
where x_id = 1
  and opening_time <= '2012-01-01 09:00' 
  and '2012-01-01 09:00' < closing_time;

-- Which tenants, regardless of building, are open on Jan 4 at 9:00 am?
select op_hours.x_id
from hours.op_hours
inner join hours.tenants on hours.tenants.tenant_id = hours.op_hours.x_id 
where opening_time <= '2012-01-04 09:00' 
  and '2012-01-04 09:00' < closing_time;

请注意,这些查询中的每一个都在半开区间上运行。您不能使用 BETWEEN 运算符,因为它在闭合区间上运行。

这种结构不能满足哪些要求?

  • 租户营业时间必须是建筑物营业时间的一个子集。除非您的 dbms 支持断言,否则无法以声明方式完成。可以用数据库中的程序代码来完成。
于 2012-05-20T01:41:07.303 回答