填补Mysql查询中时间序列空白的最佳方法

Posted

技术标签:

【中文标题】填补Mysql查询中时间序列空白的最佳方法【英文标题】:Best method to fill in gaps in a time series in a Mysql query 【发布时间】:2011-11-14 18:52:50 【问题描述】:

我需要填补一个mysql查询结果集的时间序列的空白。我正在测试使用包含时间序列的所有数据点的辅助表进行外部联接的选项(如该线程中所示:How to fill date gaps in MySQL?)。

我遇到的问题是,添加此连接会显着增加查询响应时间(从 1 秒以下缩短到 90 秒)。

这是原始查询:

select date_format(fact_data7.date_collected,'%Y-%m') as date_col
   , date_format(fact_data7.date_collected,'%d-%H:%i:%s') as time_col
   , fact_data7.batch_id,fact_data7.value as fdvalue,entities.ticker as ticker
   , date_format(fact_data7.date_collected,'%Y-%m-%d') as date_col2
   , date_format(fact_data7.date_collected,'%Y') as year 
from fact_data7  
JOIN entities on fact_data7.entity_id=entities.id  
where (1=1)
  AND ((entities.id= 963
      AND fact_data7.metric_id=1
      ))
  AND date_format(fact_data7.date_collected,'%Y-%m') > '2008-01-01'
order by date_col asc

这是添加了辅助表 (month_fill) 外连接的查询:

select date_format(month_fill.date,'%Y-%m') as date_col
    , date_format(fact_data7.date_collected,'%d-%H:%i:%s') as time_col
    , fact_data7.batch_id,fact_data7.value as fdvalue
    , entities.ticker as ticker
    , date_format(fact_data7.date_collected,'%Y-%m-%d') as date_col2
    , date_format(fact_data7.date_collected,'%Y') as year 
from fact_data7
JOIN entities
  on fact_data7.entity_id=entities.id  
RIGHT OUTER JOIN month_fill
   on date_format(fact_data7.date_collected,'%Y-%m') =  date_format(month_fill.date,'%Y-%m')  
where (1=1)
  AND (
      (entities.id= 963 AND fact_data7.metric_id=1)
      OR (entities.id is null and fact_data7.metric_id is null)
      )
  AND date_format(month_fill.date,'%Y-%m') > '2008-01-01'
order by date_col asc

我可以重组查询以提高性能是否有替代解决方案来实现我正在寻找的结果?

11/15 更新:

这是第一个查询的 EXPLAIN 输出:

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   SIMPLE  entities    const   PRIMARY     PRIMARY     4   const   1   Using filesort
1   SIMPLE  fact_data7  ALL     NULL    NULL    NULL    NULL    230636  Using where

这是第二个查询的 EXPLAIN 输出:

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   SIMPLE  month_fill  index   NULL    date    8   NULL    204     Using where; Using index; Using temporary; Using filesort
1   SIMPLE  fact_data7  ALL     NULL    NULL    NULL    NULL    230636  Using where
1   SIMPLE  entities    eq_ref  PRIMARY     PRIMARY     4   findata.fact_data7.entity_id    1   Using where

【问题讨论】:

EXPLAIN的结果在哪里? 将其添加到最初的问题中。 【参考方案1】:

甚至不考虑重构查询,我首先会在日期列 fact_data7.data_collected 和 month_fill.date 上添加索引。您正在执行的范围查询 ">" 正在减慢进程,从理论上讲,添加索引应该会提高性能,但您需要足够的记录,否则管理索引只会因为管理索引所涉及的处理而减慢。

查看这个mysql文档http://dev.mysql.com/doc/refman/5.0/en/optimization-indexes.html

我不确定你想要达到什么目的,但你可以尝试使用 mysql 的ifnull(value1,value2) 函数来实现。您的查询可能类似于以下内容:

select ifnull(date_format(fact_data7.date_collected,'%Y-%m'),date_format(month_fill.date,'%Y-%m')) as date_col, 
date_format(fact_data7.date_collected,'%d-%H:%i:%s') as time_col, 
fact_data7.batch_id,
fact_data7.value as fdvalue,
entities.ticker as ticker,
date_format(fact_data7.date_collected,'%Y-%m-%d') as date_col2 ,
date_format(fact_data7.date_collected,'%Y') as year 
from fact_data7 , month_fill
JOIN entities on fact_data7.entity_id=entities.id  
where ((entities.id= 963 AND fact_data7.metric_id=1) OR (entities.id is null and fact_data7.metric_id is null))
and date_format(fact_data7.date_collected,'%Y-%m') =  date_format(month_fill.date,'%Y-%m') --you will need a condition similar to this depends on the data
AND date_format(fact_data7.date_collected,'%Y-%m')>'2008-01-01'
order by date_col asc

【讨论】:

我已经在 fact_data7 中的 date_collected 上有一个索引。我刚刚在帮助表中的日期字段上添加了一个索引,但该表相对较小(240 行 vs ~20000 in fact_data7)并且索引对性能没有任何影响。 你能用第二个连接计算第二个查询吗?我怀疑您返回的记录可能是预期的 240 倍? 它不是在做笛卡尔积,如果那是你所怀疑的。第一个查询返回 18958 行,第二个查询返回 19055 行(填空月份)。【参考方案2】:

我认为值得尝试重写 where 以便不使用 date_format(date_collected) 。你说你在这个字段上有一个索引,但它从未使用过(字段是函数的参数, MySQL 不支持基于函数的索引)

【讨论】:

以上是关于填补Mysql查询中时间序列空白的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章

用电量查询填补时间空白

如何显示mysql分组中时间最大的一行记录

填补表中值之间的空白 - MySQL

Mysql查询将返回数据库中时间戳之间的空闲槽

SQL 查询以填补跨时间缺失的空白并获取最后一个非空值

填补xts中日期之间的空白