0

我有一个公寓预订系统,需要逐个属性地生成一个日期期间内占用天数与空置天数的报告。

请记住,在所选时间段内,某些预订可能在开始/结束日期之前和/或之后结束。

我发现了这个 -一个月内一个日期范围内的 MySQL 天数(预订表) - 这是正确的,但我不希望它是按月固定的,而是在两个可变日期之间。

理想情况下,我只想使用 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

这可行,但是如果有重叠的预订,那么它会计算两次天数。我怎样才能防止这种情况?

4

2 回答 2

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 天的日期范围,但很容易扩展以涵盖更多。

于 2013-07-10T12:06:28.397 回答
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
于 2013-07-10T12:13:02.873 回答