Mysql “Arftul软件”“查找序列的开始和结束日期”

Mysql “Arftul软件”“查找序列的开始和结束日期”,mysql,left-join,date-range,cross-join,Mysql,Left Join,Date Range,Cross Join,编辑:我将标题从“基于最小和最大日期和持续时间查找最便宜的可用日期”改为“MySQL PHP”,目的是使问题更加通用,希望其他有此问题的人能够找到解决方案。下面是我原始问题的全部内容,但在这里我将重新编写它,因为现在我明白了问题是什么,我可以更好地解释它。我自己也添加了答案 重写问题 Artful软件网站有一个查询示例,我希望使用该查询来查找房间可用的日期序列,但是由于某种原因,该查询会分解从一个月到下一个月的顺序结果。例如,如果某个房间在6月28日至7月3日之间可用,它将返回两个结果: 28t

编辑:我将标题从“基于最小和最大日期和持续时间查找最便宜的可用日期”改为“MySQL PHP”,目的是使问题更加通用,希望其他有此问题的人能够找到解决方案。下面是我原始问题的全部内容,但在这里我将重新编写它,因为现在我明白了问题是什么,我可以更好地解释它。我自己也添加了答案

重写问题

Artful软件网站有一个查询示例,我希望使用该查询来查找房间可用的日期序列,但是由于某种原因,该查询会分解从一个月到下一个月的顺序结果。例如,如果某个房间在6月28日至7月3日之间可用,它将返回两个结果:

28th - 30th June
1st - 3rd July
当它应该只返回这些日期的一个结果时,因为它们是连续的。为什么会发生这种情况?我该如何预防

原详细问题

给定一个最短到达日期、最长离开日期和停留时间,我想找出在这段时间内的任何时间都有哪些房间可用,并返回最便宜的日期

比如说。用户希望在5月25日至6月30日期间的任何时间入住14晚。首先,我需要检查我的哪些房间在5月25日至6月30日期间至少有14个连续可用的晚上,然后对于每个有可用的房间,我想显示哪些日期是14个晚上最便宜的住宿时间

我有:

日期表 每个房间都具有唯一Id的房间表 一个包含roomId、开始日期和结束日期以及价格的费率表。价格在日期范围内设置,然后每个日历年有一个标准价格,该价格用于任何不在设置费率日期范围内的日期 带有室友、开始和结束日期的预订表。 特别是使用artfulsoftware.com上的示例。向下滚动到“Forum contributor laptop alias posted this solution”(论坛贡献者笔记本电脑别名发布此解决方案)部分,查看我迄今为止使用的加入示例。这是我试图找出哪些房间至少有正确数量的连续可用日期;它没有试图找到最便宜的:

SELECT r.`id`
FROM `rooms` r
RIGHT JOIN(
  SELECT a.roomId, a.date AS startdate, MIN( c.date ) AS enddate, DATEDIFF(MIN( c.date ), a.date)+1 AS nights
  FROM (
    SELECT r.id AS roomId, d.date AS date
    FROM dates AS d
    CROSS JOIN rooms AS r WHERE
    NOT EXISTS ( SELECT id, roomId
      FROM bookings AS b
      WHERE d.date BETWEEN b.start_date AND b.end_date
      AND r.Id = roomId )
    AND d.date BETWEEN :minArr1 AND :maxDept1
  ) AS a

  LEFT JOIN(
    SELECT r.id AS roomId, d.date AS date
    FROM dates AS d
    CROSS JOIN rooms AS r WHERE
    NOT EXISTS (
      SELECT id, roomId
      FROM bookings AS b
      WHERE d.date BETWEEN b.start_date AND b.end_date
      AND r.Id = roomId
    )
    AND d.date BETWEEN :minArr2 AND :maxDept2
  ) AS b
  ON a.date = b.date + 1 AND a.roomId = b.roomId

  LEFT JOIN (
    SELECT r.id AS roomId, d.date AS date
    FROM dates AS d
    CROSS JOIN rooms AS r WHERE
    NOT EXISTS (
      SELECT id, roomId FROM bookings AS b
      WHERE d.date BETWEEN b.start_date AND b.end_date
      AND r.Id = roomId
    )
    AND d.date BETWEEN :minArr3 AND :maxDept3
  ) AS c
  ON a.date <= c.date AND a.roomId = c.roomId

  LEFT JOIN (
    SELECT r.id AS roomId, d.date AS date
    FROM dates AS d
    CROSS JOIN rooms AS r WHERE
    NOT EXISTS (
      SELECT id, roomId
      FROM bookings AS b
      WHERE d.date BETWEEN b.start_date AND b.end_date
      AND r.Id = roomId
    )
    AND d.date BETWEEN :minArr4 AND :maxDept4
  )AS d ON c.date = d.date - 1 AND c.roomId = d.roomId

  WHERE b.date IS NULL
  AND c.date IS NOT NULL
  AND d.date IS NULL
  GROUP BY a.date, a.roomId
)AS results
on results.roomId = r.Id
WHERE nights >= :n
现在我的问题是,由于某种原因,当我们到达月末时,这会打断连续的日期。例如,假设一个房间从5月25日到6月14日一直可用,而不是在20个晚上收回一个结果,它将收回两个:

5月25日至31日6晚 6月1日至14日13晚
我不知道它为什么这样做或者如何修复它,但我认为如果我能找到这个问题的解决方案,它将解决我问题的前半部分。第二种方法是通过查看费率表,找到返回期间最便宜的14晚住宿,但每次只需做一件事。

经过反复思考,解决方案很简单。Artful软件页面上的例子很有效,因为他只处理序列号,而不是日期。为了处理日期,您必须更改+1和-1,以便添加和减去日期。以下是该站点的原始解决方案:

SELECT 
  a.id AS Start, 
  MIN( c.id ) AS End 
FROM tbl AS a
LEFT JOIN tbl AS b ON a.id = b.id + 1
LEFT JOIN tbl AS c ON a.id <= c.id
LEFT JOIN tbl AS d ON c.id = d.id - 1
WHERE b.id IS NULL 
  AND c.id IS NOT NULL
  AND d.id IS NULL
GROUP BY a.id; 
为了使此功能适用于日期,只需使用:

SELECT 
  a.`date` AS Start, 
  MIN( c.`date` ) AS End 
FROM dates AS a
LEFT JOIN dates AS b ON a.`date` = DATE_ADD(b.`date`,INTERVAL 1 DAY)
LEFT JOIN dates AS c ON a.`date` <= c.`date`
LEFT JOIN dates AS d ON c.`date` = DATE_ADD(d.`date`,INTERVAL -1 DAY)
WHERE b.`date` IS NULL 
  AND c.`date` IS NOT NULL
  AND d.`date` IS NULL
GROUP BY a.`date`; 

你能用PHP处理结果吗?您的SQL是好的,虽然分解周期不方便,但如果每个月的比率不同,那么保持粒度可能会更好。如果不是,那么它们可以是任何日期。今年有一个标准价格,其他价格在任何时间段内都有设置。仍然可以用PHP处理您的响应并以这种方式连接这些时间段吗?不确定,我想在PHP中什么都有可能。我只是想一定有办法改进这个SQL来解决我的问题,因为根据我在artfulsoftware网站上读到的内容,我认为这就是这个问题的重点。我不明白为什么它在月底就破裂了。如果我这样做了,也许我可以停止它,我怀疑这需要重新设计查询。使用PHP可能会更容易