2017-09-26 97 views
1

我有一个表,其中有两列包含特定事件的time_fromtime_to。两列的类型都是TINYINT(2)。对于eaxmple总结所有时间段

id time_from time_to 
__________________________ 
11  8   14 
18  12   17  
44  20   24 

某些时段重叠。我需要总结所有时间,并确保我不重复计算重叠时间。

不幸的是,我不能改变列的类型,必须与我得到的。我怎样才能做到这一点?

预期的结果是这样的:

14 - 8 = 6 
17 - 12 = 5 
24-20 = 4 

重叠是2个小时(12 - 14)

总计:6 + 5 + 4 - 2 = 13

+0

那么,什么是与你在这里显示的预期输出? '6 + 5 + 4'或'6 + 2 + 4'(忽略12,13和14,因为ID 11包含它)?或者是其他东西? – chris85

+0

@ chris85我已更新我的帖子。我确实要数12,13 14,但只有一次。 – santa

回答

3

我假设您的time_fromtime_to列代表1到24范围内的小时数。

编辑。正如你澄清的那样,我假设20,24包括四个小时,即20,21,22,23。每个范围不包括提到的最后一小时:[20,24)

您可以用序列表解决这个问题。就是这个。 (http://sqlfiddle.com/#!9/57cf7f/4/0

 SELECT 1 seq 
     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 
     UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12 UNION ALL SELECT 13 
     UNION ALL SELECT 14 UNION ALL SELECT 15 UNION ALL SELECT 16 UNION ALL SELECT 17 
     UNION ALL SELECT 18 UNION ALL SELECT 19 UNION ALL SELECT 20 UNION ALL SELECT 21 
     UNION ALL SELECT 22 UNION ALL SELECT 23 UNION ALL SELECT 24 

在MariaDB的,它是建立在:表seq_1_to_24是它。

像这样加入你的另一张桌子,你在另一张桌子的每一行中每小时得到一行。 ()

SELECT seq.seq, t.* 
    FROM (
      SELECT 1 seq 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 
         UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12 UNION ALL SELECT 13 
         UNION ALL SELECT 14 UNION ALL SELECT 15 UNION ALL SELECT 16 UNION ALL SELECT 17 
         UNION ALL SELECT 18 UNION ALL SELECT 19 UNION ALL SELECT 20 UNION ALL SELECT 21 
         UNION ALL SELECT 22 UNION ALL SELECT 23 UNION ALL SELECT 24 
     ) seq 
    JOIN times t ON seq.seq >= t.time_from 
       AND seq.seq < t.time_to 

最后,总结与COUNT(DISTINCT seq) hours,你会得到的,在你的原始表出现在一个或多个时间间隔的小时数。 (http://sqlfiddle.com/#!9/57cf7f/10/0

SELECT COUNT(DISTINCT seq) hours 
FROM (
SELECT seq.seq, t.* 
    FROM (
      SELECT 1 seq 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 
         UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12 UNION ALL SELECT 13 
         UNION ALL SELECT 14 UNION ALL SELECT 15 UNION ALL SELECT 16 UNION ALL SELECT 17 
         UNION ALL SELECT 18 UNION ALL SELECT 19 UNION ALL SELECT 20 UNION ALL SELECT 21 
         UNION ALL SELECT 22 UNION ALL SELECT 23 UNION ALL SELECT 24 
     ) seq 
    JOIN times t ON seq.seq >= t.time_from 
       AND seq.seq < t.time_to 
) a 

下面是它在MariaDB中的样子。

SELECT COUNT(DISTINCT seq) hours 
FROM (
SELECT seq.seq 
    FROM seq_1_to_24 seq 
    JOIN times t ON seq.seq >= t.time_from 
       AND seq.seq < t.time_to 
) a 
+0

这太好了!小时20 - 24是开始结束范围,所以它只有4个。 – santa

+0

您的规格不清楚当天00:00 - 00:59的时间。当然,我的解决方案当然是有缺陷的。我的序列表可能需要一个'0'来代替'24'。教训:仔细规格的重要性。 –

+0

这是高于我的技能水平,太酷了!谢谢。 P.S>我从来没有听说过mariaDB,但它看起来相当令人印象深刻。绝对有一个好主意,让你的选择知道。 – santa

0

以下解决方案假定time_fromtime_to都是升序排列,并且只出现相邻行之间的重叠:

# Without touching the `id` column, I create a sequential column `my_id`, assuming that the name of the table is `times`: 

alter table times add my_id int(3) not null after id; 
SET @count = 0; 
UPDATE times SET my_id = @count:= @count + 1; 

# I use a temporary table `times2` that starts with the second row of the `times_from` column; Here, 9999 can be any large enough number: 

create temporary table times2 as select my_id, time_from from times limit 1, 9999; 

# Now I add a new column `time_end` to the original table. I update it with data from `time_from` column but 'shifted' by one row upwards: 

alter table times add time_end int(2) not null; 
update times a join times2 b on a.my_id = (b.my_id - 1) set a.time_end = b.time_from; 

# Ensure that the value of `time_end` in the last row is not zero: 

update times set time_end = time_to order by my_id desc limit 1; 

# The required value is taken by using the minimum of columns `time_from` and `time_end`: 

select sum(LEAST(time_to,time_end) - time_from) from times; 

# Uncomment below to clean up just after getting the results: 
    # alter table times drop column my_id; 
    # alter table times drop column time_end;