【问题标题】:MySQL Fill in missing dates between two dates for a given statusMySQL 为给定状态填充两个日期之间的缺失日期
【发布时间】:2015-12-31 10:34:48
【问题描述】:

我有一个项目数据集。项目从头到尾更改状态,状态更改的日期记录在表中(表名为“事件”-不是我的选择)。看起来像这样(简化):

Date        Status
2015-06-01  Start
2015-06-03  Stage 2
2015-06-07  Stage 3

在任何给定的日期范围内(动态确定),我希望能够查看哪些项目处于哪个状态。但是,对数据使用 BETWEEN 或其他查询只会拉出那些在此期间状态更改的项目,而不是仍处于给定状态的项目。

我目前在 Excel 中创建了一个非常笨重的解决方案,它在状态更改日期之间将行复制到新行中,如下所示:

Date          Status  
2015-06-01    Project start
2015-06-02    Project start (copied)
2015-06-03    Stage 2 
2015-06-04    Stage 2 (copied)
2015-06-05    Stage 2 (copied)
2015-06-06    Stage 2 (copied)
2015-06-07    Stage 3

此解决方案允许我查询项目的状态,例如 2015-06-06,并查看它仍处于第 2 阶段。

有什么方法可以使用 mySql 来提取相同的数据,但作为查询的输出?我听说有人建议使用日历表,但我不确定它是如何工作的。我还看到有人推荐交叉连接,但同样,我无法从描述中理解它是如何工作的。

提前感谢您的帮助!

【问题讨论】:

    标签: mysql date


    【解决方案1】:

    计划

    • 通过在日历期间交叉连接数字和 date_add 创建日历表..
    • 将您的数据加入日历源,日期为
    • 获取最大日期
    • 加入原始数据源以获取状态

    设置

    drop table if exists calendar_t;
    CREATE TABLE calendar_t (
      id integer primary key auto_increment not null,
      `date` date not null,
      day varchar(9) not null,
      month varchar(13) not null,
      `year` integer not null
    );
    
    drop view if exists digits_v;
    create view digits_v
    as
    select 0 as n
    union all
    select 1
    union all
    select 2
    union all
    select 3
    union all
    select 4
    union all
    select 5
    union all
    select 6
    union all
    select 7
    union all
    select 8
    union all
    select 9
    ;
    
    insert into calendar_t
    ( `date`, day, month, `year` )
    select 
    date_add('2015-01-01', interval 100*a2.n + 10*a1.n + a0.n day) as `date`,
    dayname(date_add('2015-01-01', interval 100*a2.n + 10*a1.n + a0.n day)) as day,
    monthname(date_add('2015-01-01', interval 100*a2.n + 10*a1.n + a0.n day)) as month,
    year(date_add('2015-01-01', interval 100*a2.n + 10*a1.n + a0.n day)) as `year`
    from
    digits_v a2
    cross join digits_v a1
    cross join digits_v a0
    order by date_add('2015-01-01', interval 100*a2.n + 10*a1.n + a0.n day)
    ;
    
    drop table if exists example;
    create table example
    (
      `date` date not null,
      status varchar(23) not null
    );
    
    insert into example
    ( `date`, status )
    values
    ( '2015-06-01',  'Start'   ),
    ( '2015-06-03',  'Stage 2' ),
    ( '2015-06-07',  'Stage 3' )
    ;
    

    查询

    select cal_date, mdate, ex2.status
    from
    (
    select cal_date, max(ex_date) as mdate
    from
    (
    select cal.`date` as cal_date, ex.`date` as ex_date
    from calendar_t cal
    inner join example ex
    on ex.`date` <= cal.`date`
    ) maxs
    group by cal_date
    ) m2
    inner join example ex2
    on m2.mdate = ex2.`date`
    -- pick a reasonable end date for filtering..
    where cal_date <= date('2015-06-15')
    order by cal_date
    ;
    

    输出

    +------------------------+------------------------+---------+
    |        cal_date        |         mdate          | status  |
    +------------------------+------------------------+---------+
    | June, 01 2015 00:00:00 | June, 01 2015 00:00:00 | Start   |
    | June, 02 2015 00:00:00 | June, 01 2015 00:00:00 | Start   |
    | June, 03 2015 00:00:00 | June, 03 2015 00:00:00 | Stage 2 |
    | June, 04 2015 00:00:00 | June, 03 2015 00:00:00 | Stage 2 |
    | June, 05 2015 00:00:00 | June, 03 2015 00:00:00 | Stage 2 |
    | June, 06 2015 00:00:00 | June, 03 2015 00:00:00 | Stage 2 |
    | June, 07 2015 00:00:00 | June, 07 2015 00:00:00 | Stage 3 |
    | June, 08 2015 00:00:00 | June, 07 2015 00:00:00 | Stage 3 |
    | June, 09 2015 00:00:00 | June, 07 2015 00:00:00 | Stage 3 |
    | June, 10 2015 00:00:00 | June, 07 2015 00:00:00 | Stage 3 |
    | June, 11 2015 00:00:00 | June, 07 2015 00:00:00 | Stage 3 |
    | June, 12 2015 00:00:00 | June, 07 2015 00:00:00 | Stage 3 |
    | June, 13 2015 00:00:00 | June, 07 2015 00:00:00 | Stage 3 |
    | June, 14 2015 00:00:00 | June, 07 2015 00:00:00 | Stage 3 |
    | June, 15 2015 00:00:00 | June, 07 2015 00:00:00 | Stage 3 |
    +------------------------+------------------------+---------+
    

    sqlfiddle


    参考

    【讨论】:

    • 谢谢,amdixon。当我回到办公室时,我会试一试。与此同时,我将努力弄清楚它的含义:) 我以前从未使用过交叉连接,并且想知道它们的用途。我想我快要知道了。
    • 交叉连接意味着从左右数据集中获取所有记录组合。因此,如果您将具有 2 条记录的数据集与具有 5 条记录的数据集交叉连接,则最终得到具有 10 条记录的数据集。查看cross join。在这种情况下,它是一种生成数字序列的方法
    • @RyanVincent 是的,您可以将其用作日历人口的现成数据源(用于序列生成)。效率可能不是问题,因为日历负载每年最多只会发生一次。只需确保 id 序列中没有间隙..
    【解决方案2】:

    您不需要创建包含所有日期的表格。你可以改变你的表格来给出每个状态的开始和结束日期,并使用一个 between 语句。

    或使用您现有的数据。

    使用@datequery 作为您要查找其状态的日期。

    Select top 1 Status from Events
    where Date <= @datequery and Date 
    order by Date desc
    

    返回您查询日期之前的最新状态更改。

    @datequery = 2015-06-06
    
    Status
    Stage 2
    

    【讨论】:

      猜你喜欢
      • 2019-07-20
      • 2021-08-15
      • 2019-05-19
      • 1970-01-01
      • 2021-09-18
      • 1970-01-01
      • 2018-02-12
      • 1970-01-01
      • 2015-07-01
      相关资源
      最近更新 更多