2012-06-14 50 views
2

我正在用PHP和MySQL构建一个简单的可用性日历。MySQL可用性日历 - 计算免费日期的方法?

我已存储的可用日期为属性表(目前个个都是7天块)

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

并预订日期的表,它引用了我的available_datesavailable_id

bookings 
    booking_id INT 
    available_id INT 

    ***user details*** 

我打算为每个属性添加行,以标记哪些日期可以预订,然后在有人预订时设置预订标志。

我想要做的是显示没有可用性设置的日期列表(在x天内,在这种情况下为7块),因此日期不会显示在该表中 - 在接下来的24个月或者。

我有麻烦缠绕在此我的头,我知道还有一个更简单的方式来做到这一点,我通过每个属性,然后每7天循环块,等等等等

的第一想法

任何人都可以启发我吗?


更新:

由于@ZaneBien的辉煌和全面的答复,我设法用他yeardate表&程序得到我需要的结果。 我所做的是当需要显示没有可用性设置的日期的页面被请求时,如果CURYEAR()+2没有任何值,PHP会调用该过程来添加更多的yeardates。

然后让我的结果,赞恩的查询稍加修改的版本:

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子句查询这样我就可以在每星期六的星期六到星期六的每个星期六之间得到不同的结果。

所以不是:

+------------+------------+ 
| 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 | 

这是正是我需要什么。再次感谢Zane提供了一个很好的答案。

+0

因此,在用户预订日期块(将新记录插入到预订表中)后,您是否立即将该日期的预订标志设置为1?另外,如果用户预定3周,你会怎么做?他们可以吗?您是否在预订表中插入3行来预订3个区块中的每一个? –

+0

最初在阅读你的问题之后,我以为你只是想要所有'available_dates'中没有预订的记录(WHERE预订= 0或NOT IN等),但仔细阅读后,我会想*你想要什么在接下来的两个**年中**都是7天的**,**在'availability_dates'表中不是**。它是否正确? –

+0

@ZaneBien是的,他们可以预订> 1日期,预订的每个区块将在预订表中排成一行。是的,我知道这不是最清晰的问题,这个问题相当复杂(至少对我来说),但你得到它的要点:) – jammypeach

回答

2

的记录理解你作为问题检索其范围内的当前和明年的全部7天的间隔块不重叠任何间隔块已经在available_dates现有:

要与当前和明年的每一天的工作,我们要创建一个包含DATE S的所有当前天一个单独的表(yeardates)和明年。这将有助于我们在检索查询中操作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等)。

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

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 here>是PROPERTY_ID。我们甚至可以通过简单地将其更改为WHERE property_id IN (<comma sep'd list of property_ids here>)来同时进行基于多个属性的选择。

+0

这是一个非常好的解决方案,正是我所要求的,谢谢。我会给它一个镜头,并接受,如果一切顺利:) – jammypeach

+0

这帮了我,再次感谢。尽管我会对最后一个查询做一些修改,但我会在更新完成后更新:) – jammypeach

+1

太棒了!我很高兴你得到了你需要的结果。我知道这是挑剔的......但在你修改后的查询中,这不是对'HAVING'子句的正确使用。如果'blockstart'是一个计算值,可能可以理解,因为最后评估了HAVING,但在你的情况下,'blockstart'只是'a.yeardate'的别名(你仍然可以访问它,甚至混淆后)。 'GROUP BY'也是不必要的,因为'a.yeardate'值是唯一的。因此,作为最终建议,在'WHERE'中取出GROUP BY和HAVING,并将'WEEKDAY(a.yeardate)= 5'作为第二个条件检查。 –

0

我认为你已经倒退了。

所有时间都可能提供,除非预订一空,什么已经预订在数据库中,敲那些你的结果

+0

这是问题的一部分 - 所有日期*不是*可能avaialable除非预订 - 在这种情况下,只能预订一年中的某些周。否则我会按照你的方式去做。那么如果不记录可用日期,我应该如何处理它? – jammypeach