2012-07-02 13 views
6

Mam tabelę z jedną z kolumn jako datę w formacie "RRRR-MM-DD". Czy mogę użyć opcji select, aby uzyskać wszystkie dane w zakresie miesięcznym? Powiedzmy, że chcę wszystkie dane od 2012-01-xx do 2013-04-xx. Więc jestem w zasadzie patrząc na zapytania SQL, jak ten poniżej:Wyboru PostgreSQL między zakresiem miesięcy

SELECT * FROM table WHERE date IN BETWEEN '2012-01' AND '2013-04' (INVALID QUERY) 

Od każdego miesiąca rozpoczyna się od „01” można modyfikować powyższe zapytanie, aby dostosować stan początkowy.

SELECT * FROM table WHERE date IN BETWEEN '2012-01-01' AND '2013-04' (INVALID QUERY) 

Teraz problem pochodzi z datą końcową. Muszę ręcznie obliczyć ostatnią datę dla danego miesiąca, biorąc pod uwagę wszystkie czynniki, takie jak długość miesiąca, rok przestępny itd., Ponieważ zapytanie nie powiedzie się, jeśli podana data jest nieprawidłowa. Więc obecnie robię coś takiego:

SELECT * FROM table WHERE date IN BETWEEN '2012-01-01' AND 'VALID_MONTH_END_DATE' (VALID Query) 

Chcę wiedzieć, czy istnieje sposób na uniknięcie tego ważnego obliczenia daty końcowej?

Wyjaśnienie

myślałem powyżej pierwszego dnia następnego miesiąca, ale nawet wtedy będę musiał zastosować jakąś logikę powiedzieć, czy jego grudnia, w przyszłym miesiącu byłoby stycznia przyszłego roku. Chciałem się dowiedzieć, czy możliwe jest tylko rozwiązanie SQL?

Odpowiedz

4

Jest to bardzo powszechne zapotrzebowanie środowisk raportowania.Stworzyłem kilka funkcji, aby pomieścić te date manipulacje

CREATE OR REPLACE FUNCTION public.fn_getlastofmonth (
    date 
) 
RETURNS date AS 
$body$ 
begin 
    return (to_char(($1 + interval '1 month'),'YYYY-MM') || '-01')::date - 1; 
end; 
$body$ 
LANGUAGE 'plpgsql' 
VOLATILE 
CALLED ON NULL INPUT 
SECURITY INVOKER 
COST 100; 

Następnie można użyć ...

WHERE date >= '2012-01-01' 
    AND date < fn_getlastofmonth('2013-04-01') 
10

Warto unikać BETWEEN dla porównań zakresu dat. Lepsze użycie >= i <, ponieważ działa równo z datami i datetime kolumnami/wartościami.

Jednym ze sposobów (jeśli można zbudować zewnętrznie dat):

WHERE date >= DATE '2012-01-01' 
    AND date < DATE '2013-05-01'  --- first date of the next month 

Można również użyć arytmetyki dat:

WHERE date >= DATE '2012-01-01' 
    AND date < DATE ('2013-04-01' + INTERVAL '1 MONTH') 

lub operator OVERLAPS:

WHERE (date, date) OVERLAPS 
     (DATE '2012-01-01', DATE '2013-05-01') 

You powinien również przeczytać dokumentację Postgres: Date/Time Functions and Operators

manual explains here dlaczego OVERLAPS działa w ten sposób:

Każdy okres czasu jest uważany reprezentują interwał półotwartych rozpocząć < = czas < końca, chyba że początek i koniec są równe, w którym to przypadku reprezentuje ta pojedyncza chwila. Oznacza to na przykład, że dwa przedziały czasu z tylko wspólnym punktem końcowym nie zachodzą na siebie.

+0

Overlaps działa dobrze, gdy wiem, ostatnią datę. Utknąłem, gdy ostatni miesiąc to grudzień. –

+0

Skąd masz parametry? Aplikacja? Sieć? Kolejne zapytanie? Czy masz je jako ciągi lub liczby całkowite (rok, miesiąc)? –

+1

Należy zachować ostrożność przy dodawaniu "przedziału" do "daty". Wynikiem jest 'timestamp'. Działa jednak w powyższym przykładzie. Prześlij wynik do 'date', jeśli to ma znaczenie. Dodanie interwału "n miesiąca" zawsze daje taki sam dzień miesiąca, niezależnie od faktycznej liczby dni w danych miesiącach - lub maksymalny dostępny dzień, kiedy w wyniku miesiąca będzie mniej dni. Aby dodać dokładną liczbę dni, dodaj "liczbę całkowitą" do "daty", co da "datę". –

0

Wszystkie odpowiedzi powyżej dostarczenie roztworu roboczego, ale jest niekompletna w taki czy inny. Ponieważ szukałem rozwiązania SQL (bez funkcji), łączę najlepsze wskazówki z powyższych rozwiązań.

Idealnym rozwiązaniem na moje pytanie byłoby:

SELECT * FROM table 
WHERE date >= '2012-01-01' AND date < date('2013-04-01') + interval '1 month' 

EDIT

nie używam funkcji nakładania się tutaj, bo jestem przechodzącej wartości domyślne daty rozpoczęcia i zakończenia jako „epoki ' i teraz'. Jeśli użytkownik nie określił żadnego zakresu czasu zapytania staje:

SELECT * FROM table 
WHERE date >= 'epoch' AND date < 'now' 

funkcja Nakładanie nie poradzi „epokę” i „teraz” i daje i błąd SQL podczas pracy nad doskonale kod dla obu przypadków.

PS: Zawarłem wszystkie odpowiedzi, które były poprawne i doprowadziły mnie do tego rozwiązania.

+0

Myślę, że musisz usunąć część '- interval '1 day''. Wypróbuj tak, jak jest, z datą "2013-04-30" na stole. –

+0

Szukam miesięcznych danych. Powiedz, że muszę pobrać wszystkie dane z roku, miesiąca do rrrr-mm. Datą końcową jest zawsze rrrr-mm-01, do którego dodajemy miesiąc i odejmuję jeden dzień. –

+1

Po prostu przetestuj zapytanie: 'WYBIERZ * Z tabeli GDZIE (data, data) OVERLAPS ('2012-01-01', data (" 2013-04-01 ") + interwał" 1 miesiąc "- interwał" 1 dzień ") 'gdy istnieje' date' w tabeli o wartości '2013-04-30'. Czy to jest zwracane? –

0
SET search_path=tmp; 

DROP TABLE zdates; 
CREATE TABLE zdates 
     (zdate timestamp NOT NULL PRIMARY KEY 
     , val INTEGER NOT NULL 
     ); 
-- some data 
INSERT INTO zdates(zdate,val) 
SELECT s, 0 
FROM generate_series('2012-01-01', '2012-12-31', '1 day'::interval) s 
     ; 

UPDATE zdates 
SET val = 1000 * random(); 

DELETE FROM zdates 
WHERE random() < 0.1; 

-- CTE to round the intervals down/up to the begin/end of the month 
WITH zope AS (
     SELECT date_trunc('month', zdate)::date AS zbegin 
     , date_trunc('month', zdate+interval '1 month')::date AS zend 
     , val AS val 
     FROM zdates 
     ) 
SELECT z.zbegin 
     , z.zend 
     , COUNT(*) AS zcount 
     , SUM(val) AS zval 
FROM zope z 
GROUP BY z.zbegin, z.zend 
ORDER BY z.zbegin, z.zend 
     ; 

WYNIK:

CREATE TABLE 
INSERT 0 366 
UPDATE 366 
DELETE 52 
    zbegin | zend | zcount | zval 
------------+------------+--------+------- 
2012-01-01 | 2012-02-01 |  28 | 13740 
2012-02-01 | 2012-03-01 |  28 | 14923 
2012-03-01 | 2012-04-01 |  26 | 13775 
2012-04-01 | 2012-05-01 |  25 | 11880 
2012-05-01 | 2012-06-01 |  25 | 12693 
2012-06-01 | 2012-07-01 |  25 | 11082 
2012-07-01 | 2012-08-01 |  26 | 13254 
2012-08-01 | 2012-09-01 |  28 | 13632 
2012-09-01 | 2012-10-01 |  28 | 16461 
2012-10-01 | 2012-11-01 |  23 | 12622 
2012-11-01 | 2012-12-01 |  24 | 12554 
2012-12-01 | 2013-01-01 |  28 | 14563 
(12 rows) 
Powiązane problemy