2017-09-26 96 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;