Mysql 基于SQL的时间序列时间间隔计算

Mysql 基于SQL的时间序列时间间隔计算,mysql,sql,datetime,time,Mysql,Sql,Datetime,Time,我有一个像这样的MySQL表 CREATE TABLE IF NOT EXISTS `vals` ( `DT` datetime NOT NULL, `value` INT(11) NOT NULL, PRIMARY KEY (`DT`) ); DT是唯一的日期和时间 数据样本: INSERT INTO `vals` (`DT`,`value`) VALUES ('2011-02-05 06:05:00', 300), ('2011-02-05 11:05:00', 250), (

我有一个像这样的MySQL表

CREATE TABLE IF NOT EXISTS `vals` (
  `DT` datetime NOT NULL,
  `value` INT(11) NOT NULL,
  PRIMARY KEY (`DT`)
);
DT是唯一的日期和时间

数据样本:

INSERT INTO `vals` (`DT`,`value`) VALUES
('2011-02-05 06:05:00', 300),
('2011-02-05 11:05:00', 250),
('2011-02-05 14:35:00', 145),
('2011-02-05 16:45:00', 100),
('2011-02-05 18:50:00', 125),
('2011-02-05 19:25:00', 100),
('2011-02-05 21:10:00', 125),
('2011-02-06 00:30:00', 150);
我需要这样的东西:

start|end|value
NULL,'2011-02-05 06:05:00',300
'2011-02-05 06:05:00','2011-02-05 11:05:00',250
'2011-02-05 11:05:00','2011-02-05 14:35:00',145
'2011-02-05 14:35:00','2011-02-05 16:45:00',100
'2011-02-05 16:45:00','2011-02-05 18:50:00',125
'2011-02-05 18:50:00','2011-02-05 19:25:00',100
'2011-02-05 19:25:00','2011-02-05 21:10:00',125
'2011-02-05 21:10:00','2011-02-06 00:30:00',150
'2011-02-06 00:30:00',NULL,NULL
我尝试了以下查询:

SELECT T1.DT AS `start`,T2.DT AS `stop`, T2.value AS value FROM (
  SELECT DT FROM vals
) T1
LEFT JOIN (
  SELECT DT,value FROM  vals
) T2
ON T2.DT > T1.DT ORDER BY T1.DT ASC

但它返回到结果中的许多行(29而不是9),我找不到任何方法来限制使用SQL。在MySQL中可能吗

您可以使用服务器端变量来模拟它:

select @myvar as start, end, value, @myvar := end as next_rows_start
from vals
变量按顺序从左到右解释,因此对@myvar的两个引用(start和next_rows_start)将以两个不同的值输出

只需记住在查询之前和/或之后将@myvar重置为null,否则第二次和后续运行将有错误的第一行:

select @myvar := null

您可以使用服务器端变量来模拟它:

select @myvar as start, end, value, @myvar := end as next_rows_start
from vals
变量按顺序从左到右解释,因此对@myvar的两个引用(start和next_rows_start)将以两个不同的值输出

只需记住在查询之前和/或之后将@myvar重置为null,否则第二次和后续运行将有错误的第一行:

select @myvar := null
使用子查询

SELECT
  (
     select max(T1.DT)
     from vals T1
     where T1.DT < T2.DT
  ) AS `start`,
  T2.DT AS `stop`,
  T2.value AS value
FROM vals T2
ORDER BY T2.DT ASC
但你需要准确地做到这一点

  • ORDER by必须存在,否则变量不能正确滚动
  • 该变量需要在查询中使用子查询设置为NULL,否则如果在一行中运行两次,第二次它将不会以NULL开头
使用子查询

SELECT
  (
     select max(T1.DT)
     from vals T1
     where T1.DT < T2.DT
  ) AS `start`,
  T2.DT AS `stop`,
  T2.value AS value
FROM vals T2
ORDER BY T2.DT ASC
但你需要准确地做到这一点

  • ORDER by必须存在,否则变量不能正确滚动
  • 该变量需要在查询中使用子查询设置为NULL,否则如果在一行中运行两次,第二次它将不会以NULL开头

如果表中有一个与DT(相同顺序)中的时间相对应的运行ID列,那么这将更容易实现。如果您不想更改表格,可以使用temp:

drop table if exists temp;

CREATE TABLE temp (
  `id` INT(11) AUTO_INCREMENT,
  `DT` datetime NOT NULL,
  `value` INT(11) NOT NULL,
  PRIMARY KEY (`id`)
);

insert into temp (DT,value) select * from vals order by DT asc;

select t1.DT as `start`, t2.DT as `end`, t2.value 
from  temp t2
left join temp t1 ON t2.id = t1.id + 1;

如果表中有一个与DT(相同顺序)中的时间相对应的运行ID列,那么这将更容易实现。如果您不想更改表格,可以使用temp:

drop table if exists temp;

CREATE TABLE temp (
  `id` INT(11) AUTO_INCREMENT,
  `DT` datetime NOT NULL,
  `value` INT(11) NOT NULL,
  PRIMARY KEY (`id`)
);

insert into temp (DT,value) select * from vals order by DT asc;

select t1.DT as `start`, t2.DT as `end`, t2.value 
from  temp t2
left join temp t1 ON t2.id = t1.id + 1;

好主意,但不适合我的情况(我的真实表中有一些额外的字段)。好主意,但不适合我的情况(我的真实表中有一些额外的字段)。纯SQL版本可以工作,但(尽管我付出了努力并进行了分析和索引)似乎在O(n^2)中运行。给定一个18000行的表,纯SQL版本大约需要580秒,带有变量的MySQL特定版本大约需要2.5秒。感谢您提供这两个版本。“纯SQL”版本可以工作,但是(尽管我做了很多工作,并对其进行了分析和索引)似乎运行在O(n^2)中。给定一个18000行的表,纯SQL版本大约需要580秒,带有变量的MySQL特定版本大约需要2.5秒。感谢您提供这两个版本。