2016-12-19 3 views
1

Мне нужно создать строку для каждого месяца (результат должен быть первым днем ​​месяца) между двумя датами для каждого человека в моей таблице. Например, если у меня есть следующие данные в моей исходной таблице:Создайте строку для каждого месяца между двумя датами в PostgreSQL

rowID | person  | startdate | enddate 
1  | 12345  | 2014-04-01 | 2014-11-30 
2  | 67890  | 2014-03-01 | 2014-05-01 

Я хочу, чтобы результаты в моей таблице назначения быть:

person | month 
12345 | 2014-04-01 
12345 | 2014-05-01 
12345 | 2014-06-01 
12345 | 2014-07-01 
12345 | 2014-08-01 
12345 | 2014-09-01 
12345 | 2014-10-01 
12345 | 2014-11-01 
67890 | 2014-03-01 
67890 | 2014-04-01 
67890 | 2014-05-01 

Большое спасибо за помощь.

ответ

0

Нет необходимости КТР или боковой присоединиться:

select 
    person, 
    generate_series(
     date_trunc('month', startdate), 
     enddate, '1 month' 
    )::date as month 
from rfem 
order by 1, 2 
; 
person | month  
--------+------------ 
    12345 | 2014-04-01 
    12345 | 2014-05-01 
    12345 | 2014-06-01 
    12345 | 2014-07-01 
    12345 | 2014-08-01 
    12345 | 2014-09-01 
    12345 | 2014-10-01 
    12345 | 2014-11-01 
    67890 | 2014-03-01 
    67890 | 2014-04-01 
    67890 | 2014-05-01 
+1

Используя набор функции, возвращающей в 'списке select' не рекомендуется. Таким образом ** ** действительно требуется боковое соединение. –

+0

@a_horse_with_no_name Это обескуражение предназначено для нескольких возвращаемых функций набора. –

+0

@a_horse_with_no_name Можете ли вы объяснить причину, почему функция возвращаемого набора в списке выбора не рекомендуется? Или ссылку, которая объясняет это? –

1

Рассчитать минимальные и максимальные сроки для каждого человека с первых дней месяцев, генерировать ежемесячные основанные диапазоны между этими датами, используя generate_series:

WITH date_ranges AS (
SELECT 
    person, 
    min(date_trunc('month', startdate))::timestamptz AS min_start, 
    max(date_trunc('month', enddate))::timestamptz AS max_end 
FROM person_table 
GROUP BY 1 
) 
SELECT 
    dr.person, 
    ser.month::DATE as month 
FROM date_ranges AS dr, 
    generate_series(min_start, max_end, '1 month') AS ser(month) 

Выход

person | month 
--------+------------ 
    12345 | 2014-04-01 
    12345 | 2014-05-01 
    12345 | 2014-06-01 
    12345 | 2014-07-01 
    12345 | 2014-08-01 
    12345 | 2014-09-01 
    12345 | 2014-10-01 
    12345 | 2014-11-01 
    67890 | 2014-03-01 
    67890 | 2014-04-01 
    67890 | 2014-05-01 

Как это работает? Неявный LATERAL JOIN для вызова функции заставляет вычислять для каждой строки из ввода.

Это решение учитывает, что у вас может быть более 1 строки на каждого человека с датами, и он принимает максимально возможный диапазон.

 Смежные вопросы

  • Нет связанных вопросов^_^