mysql日期序列的平均长度

问题描述 投票:1回答:2

我有一个报告,我想弄清楚,但我想在SQL语句中完成所有操作,而不需要在脚本中迭代一堆数据来完成它。

我有一个结构如下的表:

CREATE TABLE `batch_item` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `record_id` int(11) DEFAULT NULL,
  `created` DATE NOT NULL,
  PRIMARY KEY (`id`),
  KEY `record_id` (`record_id`)
);

日期字段始终为YEAR-MONTH-01。数据看起来像:

+------+-----------+------------+
|  id  | record_id |   created  |
+------+-----------+------------+
|    1 | 1         | 2019-01-01 |
|    2 | 2         | 2019-01-01 |
|    3 | 3         | 2019-01-01 |
|    4 | 1         | 2019-02-01 |
|    5 | 2         | 2019-02-01 |
|    6 | 1         | 2019-03-01 |
|    7 | 3         | 2019-03-01 |
|    8 | 1         | 2019-04-01 |
|    9 | 2         | 2019-04-01 |
+------+-----------+------------+

所以我想要做的就是不必创建一个循环脚本,找到每条记录的AVG连续月数。上面数据的示例如下:

Record_id 1 would have a avg of 4 months.
Record_id 2 would be 1.5
Record_id 3 would be 1

我可以编写一个脚本来遍历所有记录。我只是想避免这种情况。

mysql sql select
2个回答
1
投票

这是一个间隙和岛屿问题。您只需要枚举行即可。在MySQL 8+中,您将使用row_number(),但您可以在此处使用全局枚举:

select record_id, min(created) as min_created, max(created) as max_created, count(*) as num_months
from (select bi.*, (created - interval n month) as grp
      from (select bi.*, (@rn := @rn + 1) as n  -- generate some numbers
            from batch_item bi cross join
                 (select @rn := 0) params
            order by bi.record_id, bi.month
           ) bi
      ) bi
group by record_id, grp;

请注意,使用row_number()时,通常会使用partition by record_id。但是,如果以正确的顺序创建数字,则没有必要。

上面的查询得到了岛屿。对于最终结果,您需要一个更高级别的聚合:

select record_id, avg(num_months)
from (select record_id, min(created) as min_created, max(created) as max_created, count(*) as num_months
      from (select bi.*, (created - interval n month) as grp
            from (select bi.*, (@rn := @rn + 1) as n  -- generate some numbers
                  from batch_item bi cross join
                       (select @rn := 0) params
                  order by bi.record_id, bi.month
                 ) bi
            ) bi
      group by record_id, grp
     ) bi
group by record_id;

0
投票

这不是经过测试的解决方案。它应该在MySQL 8.x中进行微调,因为我不记得MySQL中的日期算术:

with
a as ( -- the last row of each island
  select *
  from batch_item
  where lead(created) over(partition by record_id order by created) is null
     or lead(created) over(partition by record_id order by created) 
    > created + 1 month -- Fix the date arithmetic here!
),
e as ( -- each row, now with the last row of its island
  select b.id, b.record_id, min(a.last_created) as end_created
  from batch_item b
  join a on b.record_id = a.record_id and b.created <= a.created
  group by b.id, b.record_id
),
m as ( -- each island with the number of months it has
  select
    record_id, end_created, count(*) as months
  from e
  group by record_id, end_created
)
select -- the average length of islands for each record_id
  record_id, avg(months) as avg_months
from m
group by record_id
© www.soinside.com 2019 - 2024. All rights reserved.