在邮政方面,由于收集的时间范围类似:
meter_id | device_id | start_at | end_at |
---|---|---|---|
meter1 | device1 | 2020-01-02 10:30 | 2025-01-02 14:00 |
meter2 | device1 | 2020-01-02 10:30 | 2020-01-02 11:30 |
meter3 | device1 | 2020-01-02 10:30 | 2020-01-03 11:30 |
我想把范围推到:
- a range for the whole days
- a range for the hours / mins before the whole days
- a range for the hours / mins after the whole days
- if the range already fits into a day then just leave it alone
因此,上述结论如下:
meter_id | device_id | start_at | end_at | remark |
---|---|---|---|---|
meter1 | device1 | 2020-01-02 10:30 | 2020-01-03 00:00 | hours at start of 1st row |
meter1 | device1 | 2020-01-03 00:00 | 2025-01-02 00:00 | whole days from 1st row |
meter1 | device1 | 2025-01-02 00:00 | 2025-01-02 14:00 | hours at end of 1st row |
meter2 | device1 | 2020-01-02 10:30 | 2020-01-02 11:30 | 2nd row left alone |
meter3 | device1 | 2020-01-02 10:30 | 2020-01-03 00:00 | hours at start of 3rd row |
meter3 | device1 | 2020-01-03 00:00 | 2020-01-03 11:30 | hours at end of 3rd row |
我撰写了一些作品,但很复杂,很简单。
www.un.org/Depts/DGACM/index_spanish.htm 是否有更简单的方式这样做?
Table and data:
CREATE TABLE IF NOT EXISTS metering_ranges (
metering_point_id text NOT NULL,
device_id text NOT NULL,
start_at timestamp(0) with time zone NOT NULL,
end_at timestamp(0) with time zone NOT NULL
);
INSERT INTO metering_ranges( metering_point_id, device_id, start_at, end_at)
VALUES
( meter1 , device1 , 2020-01-02 10:30:00 , 2025-01-02 14:00:00 ),
( meter2 , device1 , 2020-01-02 10:30:00 , 2020-01-02 11:30:00 ),
( meter3 , device1 , 2020-01-02 10:30:00 , 2020-01-03 11:30:00 );
Existing (complicated) solution
with
ranges_with_whole_days as (
SELECT
metering_point_id,
device_id,
start_at,
date_trunc( day , start_at) + interval 1 d as start_at_next_whole_day,
date_trunc( day , end_at) as end_at_whole_day,
end_at
FROM
metering_ranges
),
ranges as (
SELECT
metering_point_id,
device_id,
start_at,
CASE
WHEN start_at_next_whole_day <= end_at_whole_day THEN start_at_next_whole_day ELSE NULL
END as start_at_next_day,
CASE
WHEN end_at_whole_day >= start_at_next_whole_day THEN end_at_whole_day ELSE NULL
END as end_at_prev_day,
end_at
FROM
ranges_with_whole_days
),
ranges_bucketed AS (
-- get hours before whole day
SELECT metering_point_id, device_id, start_at, start_at_next_day as end_at
FROM ranges m
WHERE start_at_next_day IS NOT NULL
UNION
-- get whole day period
SELECT metering_point_id, device_id, start_at_next_day as start_at, end_at_prev_day as end_at
FROM ranges m
WHERE start_at_next_day IS NOT NULL AND end_at_prev_day IS NOT NULL AND start_at_next_day != end_at_prev_day
UNION
-- get hours after whole day
SELECT metering_point_id, device_id, end_at_prev_day as start_at, end_at
FROM ranges m
WHERE end_at_prev_day IS NOT NULL
UNION
-- get existing record if it fits within a day
SELECT metering_point_id, device_id, start_at, end_at
FROM ranges m
WHERE start_at_next_day IS NULL AND end_at_prev_day IS NULL
)
SELECT *
FROM ranges_bucketed
ORDER BY metering_point_id, device_id, start_at