2017-06-29 9 views
1

Ich habe zwei Spalten in der SQL-Tabelle zu erhalten, die startdate und endDateWie Monat, Jahr und Tage in postgresql zwischen zwei Terminen

Startdate   Enddate 
27-12-2015 22:30 03-01-2016 19:30 
01-01-2016 12:45 09-02-2016 18:30 

ich die resultierende Tabelle wie

Startdate   Enddate   Month  year days 
27-12-2015 22:30 03-01-2016 19:30 Dec  2015 5 
27-12-2015 22:30 03-01-2016 19:30 Jan  2016 3 
01-01-2016 12:45 09-02-2016 18:30 Jan  2016 31 
01-01-2016 12:45 09-02-2016 18:30 Feb  2016 9 
+0

Was sollte sein, den "Monat" Wert für z.B. startdatum = 2017-01-01 und enddatum = 2017-06-29? Oder der Jahreswert für startdatum = 2010-04-01 und enddatum = 2017-02-01? –

+0

@a_horse_with_no_name für den ersten Monat Wert sollte Jan, Februar, März, April, Mai, Juni mit den Tagen im Monat einschließlich der Start- und Enddatum sein. und für das zweite Datum Jahr ist 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 mit den Maonths und den Tagen jeweils. – dhS

+0

Bitte fügen Sie einen Kommentar hinzu, bevor Sie eine Anfrage stellen. – dhS

Antwort

1
erhalten möchten

Eine grobe Lösung wäre, alle Tage zu generieren und sie dann zu aggregieren (zählen). Es funktioniert, aber es ist grob im Gedächtnis. Wenn es nicht entscheidend ist, würde diese Lösung definitiv funktionieren. Die Alternative besteht darin, eine Monatsserie zu erstellen und einen Tag mit vielen Bedingungen zu vergleichen, wenn die Leistung entscheidend ist.

SELECT 
    dates.startdate::DATE, 
    dates.enddate::DATE, 
    to_char(days.s, 'Mon') AS mon, 
    to_char(days.s, 'YYYY') AS yr, 
    count(1) AS d 
FROM dates 
CROSS JOIN LATERAL (
    SELECT * FROM generate_series(dates.startdate, dates.enddate, INTERVAL '1 day') s 
) days 
GROUP BY 1, 2, 3, 4 

Auf jeden Fall hier ist die zweite Variante, die stattdessen durch Monate Schleppen (schneller, härter zu verstehen):

SELECT 
    dates.startdate::DATE, 
    dates.enddate::DATE, 
    to_char(months.startdate, 'Mon') AS mon, 
    to_char(months.startdate, 'YYYY') AS yr, 
    least(
     months.enddate::DATE - dates.startdate::DATE + 1, -- takes care of first month 
     dates.enddate::DATE - months.startdate::DATE + 1, -- takes care of last month 
     months.enddate::DATE - months.startdate::DATE + 1 -- takes care of full months from the middle of the intervals 
    ) AS "days" 
FROM dates 
-- get months as first day in that month 
CROSS JOIN LATERAL (
    SELECT * FROM generate_series(
     (to_char(dates.startdate, 'YYYY-MM-') || '01')::DATE, 
     (to_char(dates.enddate + INTERVAL '1 month', 'YYYY-MM-') || '01')::DATE - 1, INTERVAL '1 month') m 
) days 
-- get months as start date and end date 
CROSS JOIN LATERAL (
    SELECT 
     days.m::DATE AS startdate, 
     (days.m + INTERVAL '1 month')::DATE - 1 AS enddate 
) months 
+0

sollte dies funktionieren .. – dhS

+1

Ich fügte die "schnellere" Version, nur für den Fall – AlexanderMP

2

In diesem speziellen Fall eine plpgsql Funktion eine bessere Leistung als ein zur Verfügung stellen kann einfache SQL-Abfrage.

create or replace function get_months(startdate date, enddate date) 
returns table (mon text, year int, days int) 
language plpgsql as $$ 
declare d date; 
begin 
    d:= date_trunc('month', startdate); 
    while d < enddate loop 
     mon:= to_char(d, 'Mon'); 
     year:= to_char(d, 'YYYY'); 
     days:= case 
      when d+ '1month'::interval > enddate then enddate- d+ 1 
      when d < startdate then (d+ '1month'::interval)::date- startdate 
      else (d+ '1month'::interval)::date- d 
     end; 
     return next; 
     d:= d+ '1month'::interval; 
    end loop; 
end 
$$; 

Test:

with my_table(startdate, enddate) as (
    values 
    ('2015-12-27 22:30', '2016-01-03 19:30'), 
    ('2016-01-01 12:45', '2016-02-09 18:30') 
) 

select * 
from my_table, 
lateral get_months(startdate::date, enddate::date) 

    startdate  |  enddate  | mon | year | days 
------------------+------------------+-----+------+------ 
2015-12-27 22:30 | 2016-01-03 19:30 | Dec | 2015 | 5 
2015-12-27 22:30 | 2016-01-03 19:30 | Jan | 2016 | 3 
2016-01-01 12:45 | 2016-02-09 18:30 | Jan | 2016 | 31 
2016-01-01 12:45 | 2016-02-09 18:30 | Feb | 2016 | 9 
(4 rows) 
Verwandte Themen