我是SQL新手,我试图将时间间隔分成多个一小时的行,同时指示每小时间隔重叠的时间间隔。
我的数据:
validitybegin | validityend | ticketid |
---------------------+---------------------+------------------+
2017-08-31 12:22:04 | 2017-08-31 13:08:56 | ID1 |
2017-08-31 13:09:02 | 2017-08-31 16:46:51 | ID2 |
2017-08-31 13:09:10 | 2017-08-31 14:09:10 | ID3 |
我想要的是什么:
start | end | overlap | ticketid |
---------------------+---------------------+-------------+------------------+
2017-08-31 12:00:00 | 2017-08-31 13:00:00 | 38 | ID1 |
2017-08-31 13:00:00 | 2017-08-31 14:00:00 | 9 | ID1 |
2017-08-31 13:00:00 | 2017-08-31 14:00:00 | 51 | ID2 |
2017-08-31 14:00:00 | 2017-08-31 15:00:00 | 60 | ID2 |
2017-08-31 15:00:00 | 2017-08-31 16:00:00 | 60 | ID2 |
2017-08-31 16:00:00 | 2017-08-31 17:00:00 | 47 | ID2 |
2017-08-31 13:00:00 | 2017-08-31 14:00:00 | 51 | ID3 |
2017-08-31 14:00:00 | 2017-08-31 15:00:00 | 9 | ID3 |
最简单/最快的方法是什么?
答案 0 :(得分:2)
尝试这样的事情:
select t.start, t.end, t.ticketid,
CASE WHEN EXTRACT(HOUR from t.validityend::timestamp - t.validitybegin::timestamp + interval '30 second') = 0
THEN EXTRACT(MINUTE from date_trunc('minute', t.validityend::timestamp - t.validitybegin::timestamp + interval '30 second')) ELSE
CASE WHEN EXTRACT(HOUR from t.end - t.validitybegin::timestamp + interval '30 second') = 0
THEN EXTRACT(MINUTE from date_trunc('minute', t.end - t.validitybegin::timestamp + interval '30 second')) ELSE
CASE WHEN EXTRACT(HOUR from t.validityend::timestamp - t.start + interval '30 second') = 0
THEN EXTRACT(MINUTE from date_trunc('minute', t.validityend::timestamp - t.start + interval '30 second')) ELSE 60 END
END
END as overlap
from
(
select i.*, generate_series as start, generate_series + interval '1 hour' as end
from intervals i
cross join generate_series
( date_trunc('hour', i.validitybegin::timestamp)
, date_trunc('hour',i.validityend::timestamp)
, '1 hour'::interval)
) t
样本数据的结果
start end ticketid overlap
-------------------------------------------------------------------
2017-08-31 12:00:00 2017-08-31 13:00:00 ID1 38
2017-08-31 13:00:00 2017-08-31 14:00:00 ID1 9
2017-08-31 13:00:00 2017-08-31 14:00:00 ID2 51
2017-08-31 14:00:00 2017-08-31 15:00:00 ID2 60
2017-08-31 15:00:00 2017-08-31 16:00:00 ID2 60
2017-08-31 16:00:00 2017-08-31 17:00:00 ID2 47
2017-08-31 13:00:00 2017-08-31 14:00:00 ID3 51
2017-08-31 14:00:00 2017-08-31 15:00:00 ID3 9
答案 1 :(得分:1)
以上两个答案都是错误的,第一个将失败
start 2020-05-27 19:15:13.738000 end 2020-05-27 20:07:14.969000 ticketid 414
第二个在很多方面都会失败。这是我的工作解决方案:
WITH interal_periods AS (
WITH intervals AS (
...
)
SELECT t.start_time,
t.end_time,
t.ticketid,
t.validitybegin,
t.validityend,
LEAST(extract(EPOCH FROM t.end_time - validitybegin) / 60, 60) AS minutes_after_start_of_hour,
CASE
WHEN extract(EPOCH FROM t.end_time - validityend) / 60 > 0
THEN extract(EPOCH FROM t.end_time - validityend) / 60
ELSE 0
END AS minutes_before_end_of_hour
FROM (
SELECT i.*, generate_series AS start_time, generate_series + INTERVAL '1 hour' AS end_time
FROM intervals i
CROSS JOIN generate_series
(date_trunc('hour', i.validitybegin::timestamp)
, date_trunc('hour', i.validityend::timestamp)
, '1 hour'::interval)
) t
)
SELECT start_time,
end_time,
ticketid,
validitybegin,
validityend,
minutes_after_start_of_hour - minutes_before_end_of_hour as overlap
FROM interal_periods
答案 2 :(得分:0)
或者像这样:
WITH CTE AS
(
SELECT *, generate_series ( date_trunc('hour', validitybegin::timestamp)
, date_trunc('hour', validityend::timestamp)
, INTERVAL '1 hour') AS STRT,
generate_series ( date_trunc('hour', validitybegin::timestamp)
, date_trunc('hour', validityend::timestamp)
, INTERVAL '1 hour') + INTERVAL '1 hour' AS END
FROM my_data
),
CTE2 AS
(
SELECT *, ROW_NUMBER() OVER(PARTITION BY CTE.ticketid ORDER BY CTE.STRT DESC) AS RN,
COUNT(CTE.ticketid) OVER(PARTITION BY CTE.ticketid) AS CNT
FROM CTE
)
SELECT CTE2.STRT, CTE2.END,
CASE WHEN CTE2.CNT>2 THEN CASE
WHEN CTE2.RN<>1 AND CTE2.RN<>CTE2.CNT THEN 60
WHEN CTE2.RN<>1 AND CTE2.RN=CTE2.CNT THEN ROUND((EXTRACT('EPOCH' FROM CTE2.END-CTE2.VALIDITYBEGIN)/60)::decimal, 0)
ELSE ROUND((EXTRACT('EPOCH' FROM CTE2.VALIDITYEND-CTE2.STRT)/60)::decimal, 0)
END
ELSE CASE
WHEN CTE2.RN<>1 THEN ROUND((EXTRACT('EPOCH' FROM CTE2.END-CTE2.VALIDITYBEGIN)/60)::decimal, 0)
ELSE ROUND((EXTRACT('EPOCH' FROM CTE2.VALIDITYEND-CTE2.STRT)/60)::decimal, 0)
END
END AS OVERLAP, CTE2.ticketid
FROM CTE2
ORDER BY CTE2.ticketid, CTE2.STRT;