查找特定范围内的重复日期


Find repeating dates within a specific range

这是我在stackoverflow上的第一篇文章,所以如果我的问题不符合这里的规则,请告诉我

我试图在PHP/Mysql中实现一个搜索,它将查找特定时间范围内的所有条目,其中条目指定它们在哪个工作日有效。

这是数据库模型:

CREATE TABLE offer (
    offer_id int unsigned not null auto_increment,
    start int unsigned not null,
    primary key(offer_id),
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE offer_weekdays (
    offer_id int unsigned not null,
    weekday tinyint unsigned not null,
    index (offer_id),
    foreign key (offer_id) references offer(offer_id) 
          ON delete restrict ON update cascade,
    unique key (offer_id, weekday)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

offer描述了一个条目,以及从何时开始(列开始(时间戳))它可用。表*offer_weekdays*指定在哪个工作日重复输入。每个条目可能在几个不同的工作日重复。

我想实现一个函数,它获取特定时间戳范围内可用的所有条目。我解决这个问题的想法要么很慢,要么缺乏准确性。

我很感激任何提示。提前感谢!

示例

以下条目存储在数据库中:

  1. 开始时间:2011-01-11 10:00(周二),重复时间:周二
  2. 开始时间:2011-01-11 08:00(周二),重复时间:周二
  3. 开始时间:2011-01-12 12:00(周三),重复时间:周三、周四、周五

搜索的时间范围为2012-01-10 09:00(周二)至2012-01-11 11:00(周三)。

结果:

  1. 已找到
  2. 找不到。它符合工作日范围,但仅在08:00有效,但搜索时间是09:00
  3. 找不到。它在12:00有效,但搜索要到11:00

如果搜索的时间范围与一整周重叠,则应找到所有条目。

听起来你想选择

The offer starts within the search interval, OR
(the offer starts *before* search interval and 
 start time of the offer >= start time of the search range 
 as long as repeating weekdays match)

我真的,真的不确定我说得对。所以我要从不同的角度来看待这个问题。我还将使用SQL时间戳而不是Unix时代,这样每个人都可以看到发生了什么

此外,我认为报价或多或少会在午夜结束。现在似乎没有其他的假设对我来说是有意义的。

create table offerings (
  offer_id integer not null,
  offer_start timestamp not null,
  offer_end timestamp not null check (offer_end > offer_start),
  primary key (offer_id, offer_start)
);
-- Offer 1 starts 2012-01-10 10:00, ends 2012-01-10 23:59, repeats every Tue.
insert into offerings values (1, '2012-01-10 10:00', '2012-01-10 23:59');
insert into offerings values (1, '2012-01-17 10:00', '2012-01-17 23:59');
insert into offerings values (1, '2012-01-24 10:00', '2012-01-24 23:59');
insert into offerings values (1, '2012-01-31 10:00', '2012-01-31 23:59');
-- Offer 2 starts 2012-01-10 08:00, ends 2012-01-10 23:59, repeats every Tue.
insert into offerings values (2, '2012-01-10 08:00', '2012-01-10 23:59');
insert into offerings values (2, '2012-01-17 08:00', '2012-01-17 23:59');
insert into offerings values (2, '2012-01-24 08:00', '2012-01-24 23:59');
insert into offerings values (2, '2012-01-31 08:00', '2012-01-31 23:59');
-- Offer 3 starts 2012-01-11 12:00, ends 2012-01-11 23:59, repeats every Wed, Thu, Fri.
insert into offerings values (3, '2012-01-11 12:00', '2012-01-11 23:59');
insert into offerings values (3, '2012-01-12 12:00', '2012-01-12 23:59');
insert into offerings values (3, '2012-01-13 12:00', '2012-01-13 23:59');
insert into offerings values (3, '2012-01-18 12:00', '2012-01-18 23:59');
insert into offerings values (3, '2012-01-19 12:00', '2012-01-19 23:59');
insert into offerings values (3, '2012-01-20 12:00', '2012-01-20 23:59');
insert into offerings values (3, '2012-01-25 12:00', '2012-01-25 23:59');
insert into offerings values (3, '2012-01-26 12:00', '2012-01-26 23:59');
insert into offerings values (3, '2012-01-27 12:00', '2012-01-27 23:59');

现在的选择非常简单。

select * 
from offerings
where offer_start >= '2012-01-10 09:00'
  and offer_end   <= '2012-01-11 11:00';
offer_id   offer_start           offer_end
--
1          2012-01-10 10:00:00   2012-01-10 23:59:00

因此,如果您可以构建一个返回与此产品表相同结果的视图,那么只需查询该视图即可。

这里有三节课。

  1. 不要让你所拥有的东西迷惑你。寻找你需要的东西
  2. 记住复活节的问题。(仅仅因为可以计算一些东西并不意味着应该。)
  3. 在将其简化为产品表之后,我仍然不确定是否理解标准

后来,经过评论和更正。

-- PostgreSQL
create table offerings (
  offer_id integer not null,
  offer_at timestamp not null,
  primary key (offer_id, offer_at)
);
-- A little data for offer 1. Inserts for 2 and 3 are similar.
insert into offerings
select 1 offering_id, '2011-01-11 10:00'::timestamp + (n || ' days')::interval offer_at
from generate_series(0, 1000, 7) n
where '2011-01-11 10:00'::timestamp + (n || ' days')::interval  < '2012-03-01';

同样,这个查询非常简单。(幸运的是,这次实际上是正确的。)这两个查询都将只返回1号报价。

select *
from offerings
where offer_at between '2012-01-10 09:00' and '2012-01-10 11:00'
select *
from offerings
where offer_at between '2012-01-10 09:00' and '2012-01-11 11:00'

它们最好的一点是,假设底层数据是正确的,那么很明显查询做的是正确的。而且,对数据进行故障诊断要比对代码进行故障诊断容易得多。