使用MySQL和PHP计算日期期间的占用天数

时间:2013-07-10 11:25:14

标签: php mysql date statistics datediff

我有一个公寓预订系统,需要根据房产的基础生成一个日期内未开工日数和未被占用天数的报告。

请注意,在选定的时间段内,某些预订可能会在开始/结束日期之前和/或之后开始。

我发现了这个 - MySQL Number of Days inside a DateRange, inside a month (Booking Table) - 这是沿着正确的路线,但我不希望它按月固定,而是在两个可变日期之间。

理想情况下,我只想使用MySQL,但如果需要PHP,那就没关系了。

我能想到的唯一方法是单独循环每一天并检查当天是否有入住,但这看起来效率低得令人难以置信。

编辑: 我设法调整了其他问题的代码如下:

CREATE TABLE IF NOT EXISTS `view_bookings` (
`bkg_id` int(11) NOT NULL AUTO_INCREMENT,
`apt_id` int(10) NOT NULL,
`apt_name` varchar(50) NOT NULL,
`start_date` date DEFAULT NULL,
`end_date` date DEFAULT NULL,
PRIMARY KEY (`bkg_id`),
UNIQUE KEY `bkg_id_UNIQUE` (`bkg_id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

INSERT INTO `view_bookings` (`apt_id`, `apt_name`, `start_date`, `end_date`) VALUES
(1, 'Apartment One', '2012-09-02', '2013-02-05'),
(1, 'Apartment One', '2013-02-05', '2013-07-05'),
(2, 'Apartment Two', '2012-12-25', '2013-02-28'),
(2, 'Apartment Two', '2013-03-01', '2013-04-10'),
(2, 'Apartment Two', '2013-04-16', '2013-09-19'),
(3, 'Apartment Three', '2013-01-01', '2013-02-04'),
(3, 'Apartment Three', '2013-02-06', '2013-02-12'),
(3, 'Apartment Three', '2013-02-16', '2013-02-27'),
(3, 'Apartment Three', '2013-02-27', '2013-03-14'),
(3, 'Apartment Three', '2013-03-19', '2013-06-12');

SELECT
SUM(
    1 + DATEDIFF(
        LEAST(end_date, '2013-03-30'),
        GREATEST(start_date, '2013-02-01')
        )
    ) AS days,
    apt_name,
    apt_id
FROM
    view_bookings
WHERE
    start_date <= '2013-03-30'
AND '2013-02-01' <= end_date
GROUP BY
    apt_id

这样可行,但是如果预订重叠则会计算两天。我该如何防止这种情况?

2 个答案:

答案 0 :(得分:1)

可能通过生成开始日期和结束日期之间的所有日期来执行此操作,使用distinct删除重复日期。计算一下,以获得相关范围内唯一预订日期的总数。然后将该数字与日期范围内的天数一起使用,以获得可用天数。

完成每间公寓: -

SELECT apt_id, apt_name, DaysBooked AS DaysOccupied, DayNumber - DaysBooked AS DaysUnoccupied
FROM
(
    SELECT apt_id, apt_name, COUNT(*) AS DaysBooked
    FROM
    (
        SELECT DISTINCT view_bookings.apt_id, view_bookings.apt_name, DATE_ADD(view_bookings.start_date, INTERVAL units.i + tens.i * 10 + hundreds.i * 100 DAY) AS BookedDate
        FROM view_bookings
        CROSS JOIN (SELECT 0 AS i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) units
        CROSS JOIN (SELECT 0 AS i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) tens
        CROSS JOIN (SELECT 0 AS i UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) hundreds
        WHERE DATE_ADD(view_bookings.start_date, INTERVAL units.i + tens.i * 10 + hundreds.i * 100 DAY) <= view_bookings.end_date
        AND DATE_ADD(view_bookings.start_date, INTERVAL units.i + tens.i * 10 + hundreds.i * 100 DAY) BETWEEN '2013-02-01' AND '2013-02-28'
    ) Sub1
    GROUP BY apt_id, apt_name
) Sub3
CROSS JOIN 
(
    SELECT ABS(DATEDIFF('2013-02-01', '2013-02-28')) + 1 AS DayNumber -- Note that DATEDIFF is giving the difference in days but you want the figure to include the start and end dates so add 1.
) Sub2

请注意,这只能处理最多1000天的日期范围,但很容易扩展到更多。

答案 1 :(得分:1)

试试这个。如果您使用PHP,请先删除两行并在SQL语句

中使用php变量
SET @START = '2013-02-01';
SET @END = '2013-03-30';
SELECT vb.apt_id, SUM(1 + DATEDIFF(LEAST(vb.end_date, @END), GREATEST(vb.start_date, @START))) AS Days 
FROM view_bookings vb
WHERE (vb.start_date BETWEEN @START AND @END AND vb.end_date BETWEEN @START AND @END) 
OR (vb.start_date < @START AND vb.end_date BETWEEN @START AND @END)
OR (vb.start_date BETWEEN @START AND @END AND vb.end_date > @END) 
OR (vb.start_date < @START AND vb.end_date > @END) 
GROUP BY vb.apt_id