【问题标题】:Selected Intervals of dates in MySQLMySQL中选定的日期间隔
【发布时间】:2016-04-27 02:23:24
【问题描述】:

我正在使用 MySQL。我正在尝试获取属于一组日期间隔中每个间隔的预订之夜。但是有一些间隔比其他间隔更受欢迎,因此我会尽可能多地在首选间隔中度过,并用**不首选间隔**填补空白。为了说明这一点,我将在这里展示它:

给定日期:

签到 => 2016-01-16

退房 => 2016-02-08

总夜数 => 24

      Preferred |  date_from   |  date_to   | Nights     
----------------------------------------------------
     1          |  2016-01-15  | 2016-01-17 | 2
     1          |  2016-02-03  | 2016-02-10 | 6
     1          |  2016-01-20  | 2016-01-25 | 6
     0          |  2016-01-20  | 2016-01-31 | 2 (2016-01-26 and 2016-01-31 because the other nights are covered by a preferred period)
     1          |  2016-01-27  | 2016-01-30 | 4
     0          |  2016-01-15  | 2016-01-17 | 0 (these dates are covered by a the first interval which is a preferred interval )
     0          |  2016-02-01  | 2016-02-10 | 2 (just 2016-02-01 and 2016-02-02 because 03 - 08 are covered by the second interval which is a preferred interval)
     0          |  2016-01-18  | 2016-01-19 | 2

如何在 MySQL 中实现这一点?

【问题讨论】:

  • 因为 03 - 08 被第二个区间覆盖 OR 03 -10 #typo
  • @diEcho 我不认为这是一个错字 03-08 被第二个间隔覆盖 09 和 10 被忽略,因为它超出了签入/签出范围

标签: mysql date intervals


【解决方案1】:

假设您有一个包含 Preferred、date_from、date_to 列的表,并且您只是想计算夜晚数。

你可以试试这个查询。

SET @checkin = '2016-01-16';
SET @checkout = '2016-02-08';

SELECT T0.preferred,T0.date_from,T0.date_to,IFNULL(NIGHTS.nights,0) as Nights
FROM YourTable T0
LEFT JOIN
    (SELECT T1.preferred,T1.date_from,T1.date_to,COUNT(*) AS Nights
       FROM YourTable AS T1
       INNER JOIN
        (SELECT (@checkin + INTERVAL n DAY) as singleday
         FROM numbers 
         WHERE (@checkin + INTERVAL n DAY) <= @checkout)DAYS1
      ON DAYS1.singleday BETWEEN T1.date_from AND T1.date_to
      WHERE T1.preferred = 1
      OR NOT EXISTS 
      (SELECT 1
       FROM YourTable AS T
       WHERE T.preferred = 1
         AND DAYS1.singleday BETWEEN T.date_from AND T.date_to
       )
    GROUP BY T1.preferred,T1.date_from,T1.date_to
    )NIGHTS
ON T0.preferred = NIGHTS.preferred
AND T0.date_from = NIGHTS.date_from
AND T0.date_to = NIGHTS.date_to
WHERE
    T0.date_from <= @checkout
AND T0.date_to >= @checkin
;

http://sqlfiddle.com/#!9/d64344/10 您可以将 @checkout@checkin 替换为您的实际入住和退房时间。 你可以用你的实际表名替换 YourTable 出现

哦,是的,在 sqlfiddle 中,我包含了一个名为 Numbers 的表,其列 n 包含从 0 向上计数到任何最大可能逗留天数的数字。您还需要创建此表。

要创建表格编号,请使用以下内容

CREATE TABLE numbers AS
SELECT a.n+b.n+c.n+d.n+e.n+f.n+g.n+h.n+i.n as n
FROM
(SELECT 0 as n UNION SELECT 1)a,
(SELECT 0 as n UNION SELECT 2)b,
(SELECT 0 as n UNION SELECT 4)c,
(SELECT 0 as n UNION SELECT 8)d,
(SELECT 0 as n UNION SELECT 16)e,
(SELECT 0 as n UNION SELECT 32)f,
(SELECT 0 as n UNION SELECT 64)g,
(SELECT 0 as n UNION SELECT 128)h,
(SELECT 0 as n UNION SELECT 256)i;
  • 查询说明

1) 子查询 DAYS1 返回所有单个日期 从@checkin 到@checkout 范围

2) T1 与 DAYS1 WHERE 连接 首选是 1 或不存在覆盖的首选行 DAYS1 的日期

3) 然后我们做一个 COUNT(*) GROUP BY preferred,date_from,date_to 获取单日计数

4) 然后我们将结果称为 NIGHTS

5) 然后将 T0 与 NIGHTS 左连接以获得具有 0 个夜晚的偶数行

6) 并且只返回截取@checkin/@checkout 范围的 T0 行。

更新如果您的表太大,您可以尝试缩小子查询的范围,只包含您感兴趣的行,就像这样

SET @checkin = '2016-01-16';
SET @checkout = '2016-02-08';

SELECT T0.preferred,T0.date_from,T0.date_to,IFNULL(NIGHTS.nights,0) as Nights
FROM (SELECT * FROM YourTable WHERE date_from <= @checkout AND date_to >= @checkin) T0
LEFT JOIN
    (SELECT T1.preferred,T1.date_from,T1.date_to,COUNT(*) AS Nights
       FROM (SELECT * FROM YourTable WHERE date_from <= @checkout AND date_to >= @checkin) AS T1
       INNER JOIN
        (SELECT (@checkin + INTERVAL n DAY) as singleday
         FROM numbers 
         WHERE (@checkin + INTERVAL n DAY) <= @checkout)DAYS1
      ON DAYS1.singleday BETWEEN T1.date_from AND T1.date_to
      WHERE T1.preferred = 1
      OR NOT EXISTS 
      (SELECT 1
       FROM (SELECT * FROM YourTable WHERE date_from <= @checkout AND date_to >= @checkin) AS T
       WHERE T.preferred = 1
         AND DAYS1.singleday BETWEEN T.date_from AND T.date_to
       )
    GROUP BY T1.preferred,T1.date_from,T1.date_to
    )NIGHTS
ON T0.preferred = NIGHTS.preferred
AND T0.date_from = NIGHTS.date_from
AND T0.date_to = NIGHTS.date_to
;

【讨论】:

  • 它可以工作,但我的表有很多行,而且速度很慢,因为在生产环境中,视图“YourTable”(是的,这是一个包含许多列的视图,为了不复杂化,我省略了示例)有数千行。然而,令人惊讶的是,您以如此优雅的方式和如此快速地提出了该示例的解决方案!非常感谢楼主!!
  • 也许您也可以将WHERE date_from &lt;= @checkout AND date_to &gt;= @checkin 添加到内部查询中,以便在开始加入之前缩小结果范围
  • 再次检查我的答案,我用最底部的查询对其进行了更新,该查询在尝试对结果集进行任何操作之前尝试缩小结果集。
  • 如果你为 date_from 和 date_in 列创建索引,它应该会很快,有数千行
  • 是的,我做到了,还添加了其他参数以使其更窄,但即便如此,它也需要 17 秒,这只是完整查询的一部分。我有与价格相关的间隔标识符,并将这些乘以每个间隔中的夜数以获得正确的价格,然后是其他东西!但是还是很慢!!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-16
  • 2014-12-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多