2012-07-09 17 views
43

Chciałbym uzyskać listę dni między dwiema datami (łącznie z nimi) w bazy danych PostgreSQL. Na przykład, gdybym miał:Uzyskiwanie listy dat w zakresie w PostgreSQL

  • data rozpoczęcia: 29 czerwca 2012
  • data zakończenia: 03 lipca 2012

wtedy wynik powinien być:

29 june 2012 
30 june 2012 
1 july 2012 
2 july 2012 
3 july 2012 

Jakie byłyby najlepszy sposób robienia tego w PostgreSQL?

Dzięki.

Odpowiedz

59
select CURRENT_DATE + i 
from generate_series(date '2012-06-29'- CURRENT_DATE, 
    date '2012-07-03' - CURRENT_DATE) i 

lub nawet krócej:

select i::date from generate_series('2012-06-29', 
    '2012-07-03', '1 day'::interval) i 
6

ten powinien zrobić:

select date '2012-06-29' + i 
from generate_series(1, (select date '2012-07-3' - date '2012-06-29')) i 

Jeśli nie chcesz, aby powtórzyć START_DATE w rzeczach podselekcji się nieco bardziej skomplikowana:

with min_max (start_date, end_date) as (
    values (date '2012-06-29', date '2012-07-3') 
), date_range as (
    select end_date - start_date as duration 
    from min_max 
) 
select start_date + i 
from min_max 
    cross join generate_series(1, (select duration from date_range)) i; 

(Patrz odpowiedź Maniek za dużo lepsza wersja problemu "bez powtarzania")

+0

dzięki, Próbowałem je i prawie działa. Oba dostają wszystkie daty, ale pierwszy dzień (29 czerwca). Działa, jeśli zmienisz "1" w funkcji generate_series na "0" – Javi

+0

generate_series jest dla mnie nowym. –

+2

simplier, bez powtarzania: 'wybierz CURRENT_DATE + i from generate_series (data" 2012-06-29 "- CURRENT_DATE, data" 2012-07-03 "- CURRENT_DATE) i' – maniek

0

Jeśli masz już bazę danych, którą chcesz przetestować:

SELECT 
    TO_CHAR(date_column,'DD Mon YYYY') 
FROM 
    some_table 
WHERE 
    date_column BETWEEN '29 Jun 2012' AND '3 JUL 2012' 

GROUP BY date_column 
ORDER BY date_column 

Spowoduje to:

"29 Jun 2012" 
"30 Jun 2012" 
"01 Jul 2012" 
"02 Jul 2012" 
"03 Jul 2012" 
+0

Nie Nie mam dat w żadnej tabeli. To tylko lista dat, które muszę pokazać w raporcie i być może w ciągu kilku dni nie będzie żadnych danych. – Javi

0

Ta funkcja PLpg/SQL by rade:

CREATE OR REPLACE FUNCTION getDateList(date1 date, date2 date) 
RETURNS SETOF date AS 
$BODY$ 
DECLARE 
    count integer; 
    lower_limit integer := 0; 
    upper_limit integer := date2 - date1; 
BEGIN 
    FOR count IN lower_limit..upper_limit LOOP 
     RETURN NEXT date1 + count; 
    END LOOP; 
    RETURN; 
END; 
$BODY$ 
LANGUAGE plpgsql VOLATILE 
0

Jeżeli zakres data powinna pochodzić z wypowiedzi tabeli, można stosować następujące konstrukt :

DROP TABLE tbl ; 
CREATE TABLE tbl (zdate date NOT NULL); 
INSERT INTO tbl(zdate) VALUES('2012-07-01') , ('2012-07-09'); 

WITH mima AS (
     SELECT MIN(zdate)::timestamp as mi 
     , MAX(zdate)::timestamp as ma 
     FROM tbl 
     ) 
SELECT generate_series(mima.mi, mima.ma, '1 day':: interval):: date 
FROM mima 
     ; 

Rzuty są potrzebne, ponieważ generate_series() nie przyjmuje argumentów daty.

4

Na takie rzeczy jej ogólnie przydatny do stołu daty w systemie.

Podobnie jak w przypadku tabeli numerów, mogą być bardzo użyteczne i szybsze w użyciu niż generowanie dat w locie, szczególnie w przypadku skalowania do dużych zestawów danych.

Taka tabela z datami od 1900 do 2100 będzie bardzo mała, więc w magazynie nie ma zbyt wiele zapasów.

Edycja: Nie wiem, dlaczego zostanie odrzucony, prawdopodobnie będzie najlepszy do wykonania. Plus ma tak wiele innych zalet. Chcesz połączyć zamówienia z liczbami wydajności kwartałów? Jest to proste połączenie między tabelami. (Order.OrderDate -> Dates.Date -> Dates.Quarter -> PerformanceTotal.Quarter) itp. To samo dotyczy dni roboczych, takich jak ostatni dzień roboczy miesiąca lub pierwszy wtorek poprzedniego miesiąca. Jak stół z numerami, zdecydowanie poleciłbym je!

+1

Czy ta seria ma również takie elementy, jak kwartał, rok, miesiąc itd.? Bardzo przydatny, jeśli generujesz raporty o takich okresach. – BJury

+0

Tak, w przypadku takich nierównych sekwencji tabele kalendarzy prawdopodobnie byłyby ładniejsze niż 'generate_series'; Prawdopodobnie powinienem był powiedzieć "zwykle * nie ma potrzeby używania tabel kalendarza". –

26

Jako timestamp:

select generate_series('2012-06-29', '2012-07-03', '1 day'::interval); 

    generate_series  
------------------------ 
2012-06-29 00:00:00-03 
2012-06-30 00:00:00-03 
2012-07-01 00:00:00-03 
2012-07-02 00:00:00-03 
2012-07-03 00:00:00-03 

lub odlewano do date:

select (generate_series('2012-06-29', '2012-07-03', '1 day'::interval))::date; 

generate_series 
----------------- 
2012-06-29 
2012-06-30 
2012-07-01 
2012-07-02 
2012-07-03 
3
select generate_series('2012-06-29', '2012-07-03', '1 day'::interval)::date; 
+1

Proszę wyjaśnij swoje rozwiązanie, abyśmy wszyscy mogli się z niego uczyć. – Hannes

+1

Czym to się różni od odpowiedzi Clodoaldo? –