2

我正在使用 PHP 和 MySQL 构建一个简单的可用性日历。

我有一张表,其中存储了一个属性的可用日期(目前所有这些日期都是 7 天的块)

available_dates:
    start_date    DATE
    end_date      DATE
    available_id  INT PRIMARY KEY
    property_id   INT
    booked        TINYINT(1)  

还有一个参考available_id我的表的预订日期available_dates表:

bookings
    booking_id INT
    available_id INT

    ***user details***

我计划为每个属性添加行到 available_dates 以标记可以预订哪些日期,然后当有人预订该块时在该表上设置预订标志。

我想要做的是显示一个日期列表(以x天为单位,在这种情况下为 7 个),在接下来的 24 个月左右,没有设置可用性 - 因此该日期不会出现在该表中。

我很难解决这个问题,我知道有一种更简单的方法可以做到这一点,我的第一个想法是循环遍历每个属性,然后是每个 7 天的块,等等。

任何人都可以启发我吗?


更新:

感谢@ZaneBien 的出色而全面的回答,我已经设法通过使用他的yeardate表格和程序获得了我需要的结果。我所做的是,当请求需要显示没有可用性集的日期的页面时,如果没有 for ,PHP 将调用该过程以添加更多年份日期CURYEAR()+2

然后为了得到我的结果,稍微修改了 Zane 的查询:

SELECT
    a.yeardate AS blockstart,
    DATE_ADD(a.yeardate, INTERVAL 7 DAY) AS blockend
FROM
    yeardates a  
LEFT JOIN
    available_dates b 
        ON(a.yeardate BETWEEN b.start_date AND b.end_date)              
    OR      
    (DATE_ADD(a.yeardate, INTERVAL 7 DAY) BETWEEN b.start_date AND b.end_date)
WHERE
    b.date_id IS NULL AND WEEKDAY(a.yeardate)=5;

在我的情况下,这些块是 7 天,从星期六到星期六 - 所以我WHERE在查询中添加了第二个子句,以便我为每一行获得不同的 1 周星期六到星期六块,这些块一个接一个地发生。

所以而不是:

+------------+------------+
| blockstart | blockend   |
+------------+------------+
| 2012-01-01 | 2012-01-08 |
| 2012-01-02 | 2012-01-09 |
| 2012-01-03 | 2012-01-10 |
| 2012-01-04 | 2012-01-11 |

我明白了:

+------------+------------+
| blockstart | blockend   |
+------------+------------+
| 2012-01-07 | 2012-01-14 |
| 2012-01-14 | 2012-01-21 |
| 2012-01-21 | 2012-01-28 |
| 2012-01-28 | 2012-02-04 |

正是我需要的。再次感谢赞恩的精彩回答。

4

2 回答 2

2

将您的问题理解为检索当前和明年的所有 7 天间隔块,其范围不与available_dates中已经存在的任何间隔块重叠:

要处理当前和明年的所有日期,我们必须创建一个单独的表 ( yeardates),其中包含DATE当前和明年的所有日期。这将方便我们OUTER JOIN在检索查询中的操作。

定义yeardates表格和插入日期的代码:

CREATE TABLE yeardates 
(
    yeardate DATE NOT NULL,
    PRIMARY KEY (yeardate)
) ENGINE = MyISAM;

DELIMITER $$
CREATE PROCEDURE PopulateYear(IN inputyear INT)
    BEGIN
        DECLARE i INT;
        DECLARE i_end INT;
        SET i = 1;
        SET i_end = CASE WHEN inputyear % 4 THEN 365 ELSE 366 END;
        START TRANSACTION;
        WHILE i <= i_end DO
            INSERT INTO yeardates VALUES (MAKEDATE(inputyear, i));
            SET i = i + 1;
        END WHILE;
        COMMIT;
    END$$
DELIMITER ;

CALL PopulateYear(2012);
CALL PopulateYear(2013);

然后创建该表并包含当前和明年的所有日期。如果我们需要为随后的年份插入天数,只需CALL将年份作为参数(例如 2014、2015 等)再次执行该过程。

然后我们可以在available_dates表中得到不重叠块的7天块:

SELECT
    a.yeardate AS blockstart,
    DATE_ADD(a.yeardate, INTERVAL 7 DAY) AS blockend
FROM
    yeardates a
LEFT JOIN 
    available_dates b ON 
        (a.yeardate BETWEEN b.start_date AND b.end_date)
        OR
        (DATE_ADD(a.yeardate, INTERVAL 7 DAY) BETWEEN b.start_date AND b.end_date)
WHERE
    b.available_id IS NULL

这会根据所有房产的预订情况检索所有免费的 7 天区块,但如果我们只需要获取特定房产的免费 7 天区块,我们可以使用:

SELECT
    a.yeardate AS blockstart,
    DATE_ADD(a.yeardate, INTERVAL 7 DAY) AS blockend
FROM
    yeardates a
LEFT JOIN 
    (
        SELECT *
        FROM available_dates
        WHERE property_id = <property_id here>
    ) b ON 
        (a.yeardate BETWEEN b.start_date AND b.end_date)
        OR
        (DATE_ADD(a.yeardate, INTERVAL 7 DAY) BETWEEN b.start_date AND b.end_date)
WHERE
    b.available_id IS NULL

property_id在哪里<property_id here>。我们甚至可以一次基于多个属性进行选择,只需将其更改为WHERE property_id IN (<comma sep'd list of property_ids here>).

于 2012-06-14T19:39:20.900 回答
0

我想你已经把它弄反了。

除非预订,否则所有日期都可能可用,在数据库中记录已预订的内容并将其从结果中剔除

于 2012-06-14T16:12:41.140 回答