2012-07-02 15 views
6

Ho una tabella con una delle colonne come data nel formato "AAAA-MM-GG". Posso usare select per ottenere tutti i dati in un intervallo mensile? Dico che voglio tutti i dati da 2012-01-xx a 2013-04-xx. Così sto fondamentalmente alla ricerca di una query SQL come quello indicato di seguito:Postgresql seleziona nell'intervallo mese

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

Dal momento che ogni mese iniziano con '01' che posso modificare la query di cui sopra per regolare la condizione di avvio.

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

Ora la questione viene fornito con l'enddate. Devo calcolare manualmente l'ultima data per il mese dato, prendendo in considerazione tutti i fattori come la lunghezza del mese, l'anno bisestile, ecc., Poiché la query non riesce se la data specificata non è valida. Così attualmente sto facendo qualcosa di simile:

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

Voglio sapere se c'è qualche modo per evitare questo calcolo data di fine valida?

Chiarimento

ho pensato sopra il primo giorno del mese successivo, ma anche allora dovrò applicare una logica dicono, se dicembre, il mese successivo sarebbe gennaio del prossimo anno. Volevo sapere se è possibile una soluzione solo SQL?

risposta

4

Questa è una necessità molto comune in ambienti di reporting.Ho creato diverse funzioni per ospitare queste manipolazioni data

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; 

Quindi è possibile utilizzare ...

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

È consigliabile evitare BETWEEN per confronti di intervalli di date. È preferibile utilizzare >= e < poiché funziona in modo uguale con colonne/valori di data e ora.

Un modo (se si può costruire le date esternamente):

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

Si potrebbe anche usare l'aritmetica delle date:

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

o OVERLAPS dell'operatore:

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

È dovrebbe anche leggere la documentazione di Postgres: Date/Time Functions and Operators

Il manual explains here perché OVERLAPS funziona in questo modo:

Ogni periodo di tempo è considerato rappresentare l'intervallo semiaperto iniziare < = tempo < fine, a meno di inizio e fine sono uguali nel qual caso rappresenta quel singolo istante. Ciò significa ad esempio che due periodi con solo un endpoint in comune non si sovrappongono.

+0

sovrapposizioni funziona bene quando so che l'ultima data. Sono bloccato quando l'ultimo mese è dicembre. –

+0

Da dove prendi i parametri? Un applicazione? La rete? Un'altra domanda? Li hai come stringhe o interi (anno, mese)? –

+1

Occorre prestare attenzione quando si aggiunge un intervallo a una data. Il risultato è un 'timestamp'. Funziona nell'esempio sopra, però. Trasmetti il ​​risultato a 'date', se è importante. L'aggiunta di un intervallo "n mese" risulta sempre nello stesso giorno del mese, indipendentemente dal numero effettivo di giorni nei mesi interessati o dal giorno massimo disponibile, quando il mese risultante ha meno giorni. Per aggiungere un numero esatto di giorni, aggiungi un 'intero 'a una' data', questo si traduce in un 'date'. –

0

Tutte le risposte sopra forniscono una soluzione di lavoro, ma è incompleta in un modo o l'altro. Dal momento che stavo cercando una soluzione solo SQL (nessuna funzione), sto combinando i migliori consigli delle soluzioni di cui sopra.

La soluzione ideale per la mia domanda sarebbe:

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

EDIT

non sto usando la funzione di sovrapposizione qui perché sto passando i valori di data di default per inizio e fine come 'epoca ' e adesso'. Se l'utente non ha specificato alcun intervallo di tempo la domanda diventa:

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

funzione di sovrapposizione non è in grado di gestire 'epoca' e 'ora' e dà e l'errore SQL durante il lavoro codice di cui sopra perfettamente per entrambi i casi.

PS: ho svalutato tutte le risposte che erano corrette in un modo e mi ha portato a questa soluzione.

+0

Penso che sia necessario rimuovere la parte '- interval '1 day''. Provalo così com'è, con una data ''2013-04-30'' sul tavolo. –

+0

Sto cercando dati mensili. Diciamo che ho bisogno di ottenere tutti i dati da qualche anno, mese a aaaa-mm. La mia data di fine è sempre aaaa-mm-01 a cui aggiungo un mese e sottraggo un giorno. –

+1

Basta testare la query: 'SELECT * FROM tabella WHERE (date, date) OVERLAPS ('2012-01-01', data ('2013-04-01') + intervallo '1 mese' - intervallo '1 giorno') 'quando c'è una' date' nella tabella con valore '2013-04-30'. È restituito? –

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 
     ; 

RISULTATO:

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)