计算温度低于 0 的连续天数

Posted

技术标签:

【中文标题】计算温度低于 0 的连续天数【英文标题】:count consecutive days where temp is below 0 【发布时间】:2014-11-19 18:02:51 【问题描述】:

我正在尝试计算我的数据库中温度低于 0 的连续天数。我可以使用选择计数而不是连续天数来获得低于 0 的总天数。然后我希望能够显示第一天和最后一天以及计数。 该表每分钟更新一次。

假设我有一个最小的表,例如:

    datetime            temp
    11/14/2014 7:21:31  -2.4
    11/14/2014 7:22:31  -2.4
    11/15/2014 5:03:31  2.4
    11/15/2014 5:04:31  2.4
    11/16/2014 5:10:31  -0.2
    11/16/2014 5:11:31  -0.2
    11/17/2014 5:13:31  -0.2
    11/17/2014 5:14:31  -0.2
    11/18/2014 5:15:31  2
    11/18/2014 5:16:31  2

在示例中,连续天数为 2,第一个日期为 11/16/2014,最后一个日期为 11/17/2014,总天数(我可以得到)为 3。

感谢收看。

编辑: 我想要最长的连续连胜纪录。至于我是如何获取数据的,这是一个简单的选择语句: 从 mytable 中选择 datetime、temp;

【问题讨论】:

这是 mysql,所以那些不是日期 ;-) 如果有超过一组连续的日子——比如 14-15 和 17-18,会怎样。 是的,我刚刚从样本中导出了日期和时间。这是一个日期时间列。至于你的问题,我正在寻找最长的连续几天,它应该是最早的(按日期计算)。 请相应地修改您的问题。 [另外,要么你想要最早的(日期方面),要么你想要最长的 - 你不能同时拥有(或者只是巧合)] - 你是如何“导出”这些的日期!?!? 【参考方案1】:

考虑以下...

数据集...

 DROP TABLE IF EXISTS my_table;

 CREATE TABLE my_table
 ( dt datetime NOT NULL PRIMARY KEY
 , temp DECIMAL(5,2) NOT NULL);

 INSERT INTO my_table VALUES
 ('2014-11-14 7:21:31',  -2.4),
 ('2014-11-14 7:22:31',  -2.4),
 ('2014-11-15 5:03:31',  2.4),
 ('2014-11-15 5:04:31',  2.4),
 ('2014-11-16 5:10:31',  -0.2),
 ('2014-11-16 5:11:31',  -0.2),
 ('2014-11-17 5:13:31',  -0.2),
 ('2014-11-17 5:14:31',  -0.2),
 ('2014-11-18 5:15:31',  2),
 ('2014-11-18 5:16:31',  2);

解决方案 1 - 如果您只想知道最长运行的长度...

 SELECT MAX(@count := IF(a.result = b.result, @count + 1, 1)) LongestRun
   FROM 
      ( SELECT DISTINCT DATE(x.dt) date
                      , COALESCE(y.temp,x.temp) < 0 result 
                   FROM my_table x 
                   LEFT 
                   JOIN my_table y 
                     ON DATE(y.dt) = DATE(x.dt) AND y.temp < 0
      ) a
  CROSS
   JOIN (SELECT @count := 0) vars
   LEFT
   JOIN 
      ( SELECT DISTINCT DATE(x.dt) date
                      , COALESCE(y.temp,x.temp) < 0 result 
                   FROM my_table x 
                   LEFT 
                   JOIN my_table y 
                     ON DATE(y.dt) = DATE(x.dt) AND y.temp < 0
      ) b
     ON b.date = a.date - INTERVAL 1 DAY 
  WHERE a.result = 1; 

 +------------+
 | LongestRun |
 +------------+
 |          2 |
 +------------+

解决方案 2 - 如果您还想要一些其他信息

 SELECT a.date start
      , MIN(c.date) end
      , DATEDIFF(MIN(c.date),a.date) + 1 LongestRun
   FROM
      ( SELECT DISTINCT DATE(x.dt) date
                      , COALESCE(y.temp,x.temp) < 0 result 
                   FROM my_table x 
                   LEFT 
                   JOIN my_table y 
                     ON DATE(y.dt) = DATE(x.dt) AND y.temp < 0
      ) a
   LEFT 
   JOIN 
      ( SELECT DISTINCT DATE(x.dt) date
                      , COALESCE(y.temp,x.temp) < 0 result 
                   FROM my_table x 
                   LEFT 
                   JOIN my_table y 
                     ON DATE(y.dt) = DATE(x.dt) AND y.temp < 0
      ) b 
     ON b.date = a.date - INTERVAL 1 DAY
    AND b.result = 1
   LEFT 
   JOIN 
      ( SELECT DISTINCT DATE(x.dt) date
                      , COALESCE(y.temp,x.temp) < 0 result 
                   FROM my_table x 
                   LEFT 
                   JOIN my_table y 
                     ON DATE(y.dt) = DATE(x.dt) AND y.temp < 0
      ) c 
     ON c.date >= a.date 
    AND c.result = 1
   LEFT 
   JOIN 
      ( SELECT DISTINCT DATE(x.dt) date
                      , COALESCE(y.temp,x.temp) < 0 result 
                   FROM my_table x 
                   LEFT 
                   JOIN my_table y 
                     ON DATE(y.dt) = DATE(x.dt) AND y.temp < 0
      ) d 
     ON d.date = c.date + INTERVAL 1 DAY
    AND d.result = 1
  WHERE a.result = 1
    AND b.date IS NULL
    AND c.date IS NOT NULL
    AND d.date IS NULL
  GROUP 
     BY a.date
  ORDER 
     BY LongestRun DESC 
  LIMIT 1;

  +------------+------------+------------+
  | start      | end        | LongestRun |
  +------------+------------+------------+
  | 2014-11-16 | 2014-11-17 |          2 |
  +------------+------------+------------+

对于聚合数据,只需发出单独的查询。

【讨论】:

为您的方法竖起大拇指,它甚至比我的还要快(2 毫秒对 5 毫秒)。【参考方案2】:

您需要使用date 字段将您的表连接到自身。像这样的:

SELECT dt, temp FROM
(
   SELECT date(t1.`datetime`) as dt, t1.temp  
   FROM mytable t1 LEFT JOIN mytable AS t2 
   ON date(t2.`datetime`) = (date(t1.`datetime`)-interval 1 day) 
) TMP WHERE temp < 0 

【讨论】:

(顺便说一句,保留字列表中明确排除了日期时间 - 但我同意这种观点,如果不是解决方案!)【参考方案3】:

使用自定义变量和一些增量逻辑来计算冻结天数(温度低于零)。这应该让你开始

SELECT 
        t1.endofstreakdate, 
        IF(t1.freeze > 0, @m := @m + 1, @m := t1.freeze) AS streak 
FROM (
        SELECT 
                DATE(t.datetime) AS endofstreakdate, 
                DAYOFYEAR(t.datetime) AS doy, 
                MIN(t.temp) AS low, 
                IF(MIN(t.temp) < 0, @i := @i + 1, @i := @i - @i) AS freeze 
        FROM (
                SELECT 
                        @i := 0, 
                        @m := 0
                ) v, 
                temperature t 
        GROUP BY doy
) t1 

LEFT JOIN (
        SELECT 
                DAYOFYEAR(t.datetime) AS doy, 
                MIN(t.temp) AS low, 
                IF(MIN(t.temp) < 0, @i := @i + 1, @i := @i - @i) AS freeze 
        FROM (
                SELECT 
                        @i := 0, 
                        @m := 0
                ) v, 
                temperature t 
        GROUP BY doy
) t2 

        ON t2.doy = t1.doy - 1

这将为您提供所有唯一日期(不含时间)的列表以及温度低于 0 的天数,例如

    endofstreakdate     streak
    2014-11-14              1
    2014-11-15              0
    2014-11-16              1
    2014-11-17              2
    2014-11-18              0

您现在可以ORDER BY streak DESC, endofstreakdate ASC LIMIT 1 获取最早出现的最长冻结期(它返回低于零的天数和该期的最后一天)。

【讨论】:

以上是关于计算温度低于 0 的连续天数的主要内容,如果未能解决你的问题,请参考以下文章

SQL:连续天数的计算方法

Hive计算最大连续登陆天数

在 SQL 中计算连续班次和天数

SQL 计算连续天数

SQL 计算最长连续登录天数

Oracle - 计算满足相同给定条件的连续天数