3

I have a set of periods like:

CREATE TABLE `periods` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `start_at` date DEFAULT NULL,
  `end_at` date DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

LOCK TABLES `periods` WRITE;

INSERT INTO `periods` (`id`, `start_at`, `end_at`)
VALUES
    (1,'2013-04-29','2013-04-30'),
    (2,'2013-05-05','2013-05-10'),
    (3,'2013-05-10','2013-05-15'),
    (4,'2013-05-15','2013-05-16'),
    (5,'2013-05-18','2013-05-19'),
    (6,'2013-05-19','2013-05-25');

UNLOCK TABLES;

My intended desire, is the most optimized way to know if a given period is fully covered by one or multiple periods.

For instance:

1) to get null for a request from 2013-04-29 until 2013-05-10, cause no period covers from 2013-04=30 to 2013-05-05

2) to get the period ids (or at least true or any content) for a request from 2013-05-06 to 2013-05-15

UPDATE: The main goal is to define if the given period (from 2013-05-06 to 2013-05-15 as per example 2) is rentable. The periods in database are available rental seasons, so if any of the day are not covered, the entire stay can not be rented.

4

4 回答 4

2

编辑:请参阅此处以获取 MySQL 工作 SQL Fiddle:SQLFiddle,这次实际上可以正常工作:-)

试试这些。最重要的是,如果Shortfall > 0那样,您就无法预订租金。

MSSQL - 这就是我的工作方式

DECLARE @start DATETIME = '2013-04-29' -- this will depend on your dateformat
DECLARE @end DATETIME = '2013-05-10'
DECLARE @days INT = DATEDIFF(D,@start, @end) -- this is how many days we actually want to stay
DECLARE @unusedDays INT = 0 -- this will be the number of unused days from the rental periods in which our start and end dates fall
SELECT  @UnusedDays = DATEDIFF(D,@end,end_at) FROM PERIODS WHERE (@end > start_at AND @end <= end_at) -- how many spare days are there in the final period?
SELECT  @UnusedDays = @UnusedDays + DATEDIFF(D,start_at, @start) FROM PERIODS WHERE (@start >= start_at AND @start < end_at) -- how many spare days are there in the start period?
SELECT  @days + @UnusedDays - SUM(DATEDIFF(D,start_at,end_at)) AS Shortfall, -- total shortfall in days. Zero if we are okay to rent
        SUM(DATEDIFF(D,start_at,end_at)) AS AvailableDays, -- total number of days available in all periods covering our chosen rental period
        @days AS DesiredDays, -- number of days we want to rent
        @UnusedDays AS WastedDays -- number of wasted days (if we start or end our rental mid-period)
FROM    PERIODS 
WHERE   (@start >= start_at AND @start < end_at) -- period in which our selected rental starts
OR      (end_at < @end AND start_at > @start) -- period completely within our selected rental
OR      (@end > start_at AND @end <= end_at) -- period in which our selected rental ends

这提供了这样的输出:

-- if you have @start = '2013-05-05'
-- and @end = '2013-05-13'
-- then you get
Shortfall AvailableDays DesiredDays WastedDays

0---------10------------8-----------2---------

-- if you have @start = '2013-04-29'
-- and @end = '2013-05-10'
-- then you get
Shortfall AvailableDays DesiredDays WastedDays

5---------6-------------11----------0---------

MySQL - 这就是你真正想要的

SET @start = '2013-04-29';
SET @end = '2013-05-10';
SET @days = DATEDIFF(@end, @start); -- this is how many days we actually want to stay
SET @UnusedDays = 0; -- this will be the number of unused days from the rental periods in which our start and end dates fall
SELECT  @UnusedDays := DATEDIFF(end_at,@end) FROM PERIODS WHERE (@end > start_at AND @end <= end_at); -- how many spare days are there in the final period?
SELECT 'hello';
SELECT  @UnusedDays := @UnusedDays + DATEDIFF(@start, start_at) FROM PERIODS WHERE (@start >= start_at AND @start < end_at); -- how many spare days are there in the start period?
SELECT 'hello';
SELECT  @days + @UnusedDays - SUM(DATEDIFF(end_at, start_at)) AS Shortfall, -- total shortfall in days. Zero if we are okay to rent
        SUM(DATEDIFF(end_at, start_at)) AS AvailableDays, -- total number of days available in all periods covering our chosen rental period
        @days AS DesiredDays, -- number of days we want to rent
        @UnusedDays AS WastedDays -- number of wasted days (if we start or end our rental mid-period)
FROM    PERIODS 
WHERE   (@start >= start_at AND @start < end_at) -- period in which our selected rental starts
OR      (end_at < @end AND start_at > @start) -- period completely within our selected rental
OR      (@end > start_at AND @end <= end_at); -- period in which our selected rental ends
于 2013-04-29T18:07:24.690 回答
1

虽然我喜欢@Dommer 的方法和包含的细节(非常感谢),但我更喜欢@snoyes 在IRC#mysql 上提供的方法。

SELECT IF(COUNT(*), false, true) AS rentable
FROM(
  SELECT
    a.end_at AS START,
    Min(b.start_at) AS END
  FROM periods AS a
  JOIN periods AS b ON a.end_at <= b.start_at
  GROUP BY a.end_at
  HAVING a.end_at < MIN(b.start_at)
) AS gaps
WHERE
  gaps.START < '2013-05-17' AND gaps.END > '2013-05-05';

一个工作SQLFiddle也是可用的。

更多详情,初步参考来自http://www.artfulsoftware.com/infotree/qrytip.php?id=577

于 2013-05-03T10:46:50.057 回答
0
Select ID from `periods`
where
    start_at >= <Your_given_start> 
    and end_at <= <Your_given_end>

不幸的是,如果有一段时间,这只会返回一些东西。

于 2013-04-29T14:58:47.283 回答
0

您可以在一个查询中完成所有操作。以下查询假定您已@StartDate定义@EndDate

关键思想是您只需要在开始日期前一天或结束日期后一天进行测试。如果有一段没有覆盖,那么它会在这段时间内出现。以下查询计算测试日期以及它们是否可用:

select TestDay, COUNT(p.id)     
from ((select p.Start_at - 1 as TestDay
       from #Periods p
       where p.start_at > @StartDate and p.start_at <= @EndDate
      )
      union all
      (select p.End_at
       from #periods p
       where p.start_at >= @StartDate and p.start_at < @EndDate
      )
     ) t left outer join
     #periods p
     on t.TestDay >= p.start_at and t.TestDay < p.end_at
group by TestDay;

这比您需要的信息更多。您只需要0第一种情况和1第二种情况。这只是p.id对上述查询中是否为 ever的测试NULL

select MAX(case when p.id is null then 1 else 0 end)  
from ((select p.Start_at - 1 as TestDay
       from #Periods p
       where p.start_at > @StartDate and p.start_at <= @EndDate
      )
      union all
      (select p.End_at
       from #periods p
       where p.start_at >= @StartDate and p.start_at < @EndDate
      )
     ) t left outer join
     #periods p
     on t.TestDay >= p.start_at and t.TestDay < p.end_at;

此查询可能有一个差一个错误(取决于结束日期是否有空房)。这样的问题很容易解决。

于 2013-05-23T14:13:47.863 回答