2014-12-10 4 views
2

У меня есть эта активность таблицаSQL/Postgres дата и время разделения/нормализации

+--------------+------------------+ 
| Field  | Type    | 
+--------------+------------------+ 
| id   | int(11) unsigned | 
| start_date | timestamp  | 
| end_date  | timestamp  | 
| ...   |     | 
+--------------+------------------+ 

Мне нужна вид, какая группа этих видов деятельности датой_начала в день, но таким образом, что, если end_date не в том же day как start_date, представление содержит запись снова, но с start_date, установленным на 00:00 следующего дня .. (и так далее, повторяется столько раз, сколько необходимо до тех пор, пока start_date не будет в тот же день, что и end_date)

В качестве примера:

если таблица деятельности содержит:

+--------------+----------------------------+----------------------------+ 
| id   | start_date     | end_date     | 
+--------------+----------------------------+----------------------------+ 
| 1   | 2014-12-02 14:12:00+00  | 2014-12-03 06:45:00+00  | 
| 2   | 2014-12-05 15:25:00+00  | 2014-12-05 07:29:00+00  |         
+--------------+----------------------------+----------------------------+ 

мнение должно содержать:

+--------------+----------------------------+----------------------------+ 
| activity_id | start_date     | end_date     | 
+--------------+----------------------------+----------------------------+ 
| 1   | 2014-12-02 14:12:00+00  | 2014-12-02 23:59:59+00  | 
| 1   | 2014-12-03 00:00:00+00  | 2014-12-03 06:45:00+00  | 
| 2   | 2014-12-05 15:25:00+00  | 2014-12-05 07:29:00+00  |         
+--------------+----------------------------+----------------------------+ 

Любая помощь будет принята с благодарностью!

PS: Я использую PostgreSQL

ответ

1

Чтобы получить нужные строки, начните с помощью set returning function вместе с lateral join. Оттуда используйте операторы CASE и date arithmetics, чтобы вывести соответствующие значения.

Вот пример, чтобы вы начали:

with data as (
    select id, start_date, end_date 
    from (values 
    (1, '2014-12-02 14:12:00+00'::timestamptz, '2014-12-03 06:45:00+00'::timestamptz), 
    (2, '2014-12-05 15:25:00+00'::timestamptz, '2014-12-05 07:29:00+00'::timestamptz) 
) as rows (id, start_date, end_date) 
) 
select data.id, 
     case days.d = date_trunc('day', data.start_date) 
     when true then data.start_date 
     else days.d 
     end as start_date, 
     case days.d = date_trunc('day', data.end_date) 
     when true then data.end_date 
     else days.d + interval '1 day' - interval '1 sec' 
     end as end_date 
from data 
join generate_series(
     date_trunc('day', data.start_date), 
     date_trunc('day', data.end_date), 
     '1 day' 
    ) as days (d) 
     on days.d >= date_trunc('day', data.start_date) 
     and days.d <= date_trunc('day', data.end_date) 

id |  start_date  |  end_date   
----+------------------------+------------------------ 
    1 | 2014-12-02 15:12:00+01 | 2014-12-02 23:59:59+01 
    1 | 2014-12-03 00:00:00+01 | 2014-12-03 07:45:00+01 
    2 | 2014-12-05 16:25:00+01 | 2014-12-05 08:29:00+01 
(3 rows) 

Как и в сторону, в зависимости от того, что вы делаете, это может сделать больше смысла для вас, чтобы использовать date range:

with data as (
    select id, start_date, end_date 
    from (values 
    (1, '2014-12-02 14:12:00+00'::timestamptz, '2014-12-03 06:45:00+00'::timestamptz), 
    (2, '2014-12-05 07:25:00+00'::timestamptz, '2014-12-05 15:29:00+00'::timestamptz) 
) as rows (id, start_date, end_date) 
) 
select data.id, 
     tstzrange(data.start_date, data.end_date) 
from data; 

id |      tstzrange      
----+----------------------------------------------------- 
    1 | ["2014-12-02 15:12:00+01","2014-12-03 07:45:00+01") 
    2 | ["2014-12-05 08:25:00+01","2014-12-05 16:29:00+01") 
(2 rows) 
+0

вы мой друг, прав. – Alexandr