使用MySQL(和PHP)搜索可用性?

使用MySQL(和PHP)搜索可用性?,php,mysql,Php,Mysql,我有两个MySQL MyIsAm表,表示出租单位和预订: 出租单位ID、名称等。。。 出租单元预订ID,F_LU_ID,开始,结束 其中F_LU_ID是该单元的外键 搜索特定时间段内可用单位的最佳方法是什么?向搜索传递开始、结束和持续时间 开始=预订的最早开始时间 结束=预订的最新结束时间 持续时间=预订的持续时间 我很想知道在MySQL中是否有可能做到这一点,但是如果没有,那么最好的方法就是在PHP中做到这一点 范例 在回答下面的答案时,我觉得一个例子将有助于解释这个问题 出租单位: 富屋12

我有两个MySQL MyIsAm表,表示出租单位和预订:

出租单位ID、名称等。。。 出租单元预订ID,F_LU_ID,开始,结束 其中F_LU_ID是该单元的外键

搜索特定时间段内可用单位的最佳方法是什么?向搜索传递开始、结束和持续时间

开始=预订的最早开始时间 结束=预订的最新结束时间 持续时间=预订的持续时间 我很想知道在MySQL中是否有可能做到这一点,但是如果没有,那么最好的方法就是在PHP中做到这一点

范例

在回答下面的答案时,我觉得一个例子将有助于解释这个问题

出租单位:

富屋123号 一些出租单元预订:

400、123、01/01/09、05/01/09-5天预订 401、123、10/01/09、20/01/09-为期10天的预订 402、123、25/01/09、30/01/09-为期5天的预订 如果我们搜索:

开始=01/01/09 完=01/02/09 持续时间=5天 然后我们想让这个单位出现。因为在搜索范围内有5天的预订

如果持续时间为10,则单位将不会显示,因为搜索范围内没有连续10个未预订的天数。

这不太好

将LettingUnitBookings加入到自身中 查找每个F_LU_ID的预订间隔的开始和结束 获取间隙的大小-可用的“插槽” 考虑不存在现有的预订“括号”合适的时隙的情况,为此添加外部离群日期。 将该投影连接到LettingUnits表,并在其中应用条件开始、结束和持续时间 我忽略了包括那些根本没有预订的预订单位

最后看起来像这样:

SELECT @StartOfWindow := '2009-01-01',
       @EndOfWindow   := '2009-02-01',
       @WindowSize    := 5
;

SELECT
    lu.Name,
    Slots.*
FROM (
    SELECT
        lub1.F_LU_ID,
        DATE_ADD( MAX( lub2.date_time ), INTERVAL 1 DAY )     AS StartOfSlot,
        DATE_SUB( lub1.date_time, INTERVAL 1 DAY )            AS EndOfSlot,
        DATEDIFF( lub1.date_time, MAX( lub2.date_time ) ) - 1 AS AvailableDays
    FROM
    ( SELECT F_LU_ID, Start AS date_time FROM LettingUnitBookings
      UNION
      SELECT F_LU_ID, CAST( '9999-12-31' AS DATE ) FROM LettingUnitBookings
    ) AS lub1,
    ( SELECT F_LU_ID, End   AS date_time FROM LettingUnitBookings
      UNION
      SELECT F_LU_ID, CAST( '1000-01-01' AS DATE ) FROM LettingUnitBookings
    ) AS lub2
    WHERE
        lub2.date_time <= lub1.date_time
    AND lub2.F_LU_ID = lub1.F_LU_ID
    GROUP BY
        lub1.F_LU_ID,
        lub1.date_time
    ) Slots
JOIN LettingUnits lu
ON   lu.ID = Slots.F_LU_ID
WHERE
    Slots.AvailableDays >= @WindowSize
AND (
   (     DATEDIFF( Slots.EndOfSlot, @EndOfWindow )     >= @WindowSize
     AND DATEDIFF( @StartOfWindow, Slots.StartOfSlot ) >= @WindowSize
   )
   OR
   (     DATEDIFF( @EndOfWindow, Slots.StartOfSlot ) >= @WindowSize
     AND DATEDIFF( Slots.EndOfSlot, @StartOfWindow ) >= @WindowSize
   )
)
希望它能适应你的需要

或者,如果预订可以在前一次预订结束的同一天开始,您可以稍微调整

SELECT
    lu.Name,
    Slots.*
FROM (
    SELECT
        lub1.F_LU_ID,
        MAX( lub2.date_time ) AS StartOfSlot,
        lub1.date_time        AS EndOfSlot,
        DATEDIFF( lub1.date_time, MAX( lub2.date_time )) AS AvailableDays
    FROM
    ( SELECT F_LU_ID, Start AS date_time FROM LettingUnitBookings
      UNION
      SELECT F_LU_ID, CAST( '9999-12-31' AS DATE ) FROM LettingUnitBookings
    ) AS lub1,
    ( SELECT F_LU_ID, End   AS date_time FROM LettingUnitBookings
      UNION
      SELECT F_LU_ID, CAST( '1000-01-01' AS DATE ) FROM LettingUnitBookings
    ) AS lub2
    WHERE
        lub2.date_time <= lub1.date_time
    AND lub2.F_LU_ID = lub1.F_LU_ID
    GROUP BY
        lub1.F_LU_ID,
        lub1.date_time
    ) Slots
JOIN LettingUnits lu
ON   lu.ID = Slots.F_LU_ID
WHERE
    Slots.AvailableDays >= @WindowSize
AND
   (     DATEDIFF( Slots.EndOfSlot, @EndOfWindow )     >= @WindowSize
     AND DATEDIFF( @StartOfWindow, Slots.StartOfSlot ) >= @WindowSize
   )
   OR
   (     DATEDIFF( @EndOfWindow, Slots.StartOfSlot ) >= @WindowSize
     AND DATEDIFF( Slots.EndOfSlot, @StartOfWindow ) >= @WindowSize
   )

以下是一个似乎有效的解决方案:

SELECT t.*, DATEDIFF(t.LatestAvailable, t.EarliestAvailable) AS LengthAvailable
FROM 
   (SELECT u.*,
      COALESCE(b1.End, @StartOfWindow) AS EarliestAvailable,
      COALESCE(b2.Start, @EndOfWindow) AS LatestAvailable
    FROM LettingUnits u
    LEFT OUTER JOIN LettingUnitBookings b1
      ON (u.ID = b1.F_LU_ID AND b1.End BETWEEN @StartOfWindow AND @EndOfWindow)
    LEFT OUTER JOIN LettingUnitBookings b2
      ON (u.ID = b2.F_LU_ID AND b2.Start BETWEEN @StartOfWindow AND @EndOfWindow
          AND b2.Start >= b1.End) -- edit: new term
    ) AS t
LEFT OUTER JOIN LettingUnitBookings x
  ON (t.ID = x.F_LU_ID AND x.Start < t.LatestAvailable AND x.End > t.EarliestAvailable)
WHERE x.ID IS NULL AND DATEDIFF(t.LatestAvailable, t.EarliestAvailable) >= @WindowSize;
使用EXPLAIN对此进行分析表明,它很好地使用了索引:

+----+-------------+------------+--------+---------------+---------+---------+-------+------+-------------------------+
| id | select_type | table      | type   | possible_keys | key     | key_len | ref   | rows | Extra                   |
+----+-------------+------------+--------+---------------+---------+---------+-------+------+-------------------------+
|  1 | PRIMARY     | <derived2> | ALL    | NULL          | NULL    | NULL    | NULL  |    9 | Using where             |
|  1 | PRIMARY     | x          | ref    | F_LU_ID       | F_LU_ID | 8       | t.ID  |    2 | Using where; Not exists |
|  2 | DERIVED     | u          | system | NULL          | NULL    | NULL    | NULL  |    1 |                         |
|  2 | DERIVED     | b1         | ref    | F_LU_ID       | F_LU_ID | 8       | const |    0 |                         |
|  2 | DERIVED     | b2         | ref    | F_LU_ID       | F_LU_ID | 8       | const |    0 |                         |
+----+-------------+------------+--------+---------------+---------+---------+-------+------+-------------------------+

通常,您希望避免强制使用filesort或temporary的优化计划,因为它们是性能杀手。使用GROUP BY的查询几乎肯定会导致这种优化,至少在MySQL中是这样。

@Bill-nice OPTIMIE-我在测试数据集中添加了一个额外的出租单元,其中包含2009/01/01-2009/01/20和2009/02/25-2009/02/28的预订,但它不会出现在使用您的查询的结果集中。马丁:我在查询中添加了一个术语来处理这个问题。它没有改变优化计划。
+-----+-------------+-------------------+-----------------+-----------------+
| ID  | Name        | EarliestAvailable | LatestAvailable | LengthAvailable |
+-----+-------------+-------------------+-----------------+-----------------+
| 123 | Foo Cottage | 2009-01-05        | 2009-01-10      |               5 |
| 123 | Foo Cottage | 2009-01-20        | 2009-01-25      |               5 |
| 456 | Bar Cottage | 2009-01-20        | 2009-01-31      |              11 |
+-----+-------------+-------------------+-----------------+-----------------+
+----+-------------+------------+--------+---------------+---------+---------+-------+------+-------------------------+
| id | select_type | table      | type   | possible_keys | key     | key_len | ref   | rows | Extra                   |
+----+-------------+------------+--------+---------------+---------+---------+-------+------+-------------------------+
|  1 | PRIMARY     | <derived2> | ALL    | NULL          | NULL    | NULL    | NULL  |    9 | Using where             |
|  1 | PRIMARY     | x          | ref    | F_LU_ID       | F_LU_ID | 8       | t.ID  |    2 | Using where; Not exists |
|  2 | DERIVED     | u          | system | NULL          | NULL    | NULL    | NULL  |    1 |                         |
|  2 | DERIVED     | b1         | ref    | F_LU_ID       | F_LU_ID | 8       | const |    0 |                         |
|  2 | DERIVED     | b2         | ref    | F_LU_ID       | F_LU_ID | 8       | const |    0 |                         |
+----+-------------+------------+--------+---------------+---------+---------+-------+------+-------------------------+
+----+--------------+---------------------+--------+---------------+---------+---------+------+------+---------------------------------+
| id | select_type  | table               | type   | possible_keys | key     | key_len | ref  | rows | Extra                           |
+----+--------------+---------------------+--------+---------------+---------+---------+------+------+---------------------------------+
|  1 | PRIMARY      | lu                  | system | PRIMARY,ID    | NULL    | NULL    | NULL |    1 |                                 |
|  1 | PRIMARY      | <derived2>          | ALL    | NULL          | NULL    | NULL    | NULL |    4 | Using where                     |
|  2 | DERIVED      | <derived3>          | ALL    | NULL          | NULL    | NULL    | NULL |    4 | Using temporary; Using filesort |
|  2 | DERIVED      | <derived5>          | ALL    | NULL          | NULL    | NULL    | NULL |    4 | Using where; Using join buffer  |
|  5 | DERIVED      | LettingUnitBookings | ALL    | NULL          | NULL    | NULL    | NULL |    3 |                                 |
|  6 | UNION        | LettingUnitBookings | index  | NULL          | F_LU_ID | 8       | NULL |    3 | Using index                     |
| NULL | UNION RESULT | <union5,6>          | ALL    | NULL          | NULL    | NULL    | NULL | NULL |                                 |
|  3 | DERIVED      | LettingUnitBookings | ALL    | NULL          | NULL    | NULL    | NULL |    3 |                                 |
|  4 | UNION        | LettingUnitBookings | index  | NULL          | F_LU_ID | 8       | NULL |    3 | Using index                     |
| NULL | UNION RESULT | <union3,4>          | ALL    | NULL          | NULL    | NULL    | NULL | NULL |                                 |
+----+--------------+---------------------+--------+---------------+---------+---------+------+------+---------------------------------+