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