2012-07-02 23 views
6

Tengo la tabla con una de las columnas con la fecha en formato 'AAAA-MM-DD'. ¿Puedo usar seleccionar para obtener todos los datos en un rango mensual? Supongamos que quiero todos los datos desde 2012-01-xx hasta 2013-04-xx. Así que estoy básicamente en busca de una consulta SQL como el que se indica a continuación:Postgresql seleccione entre el rango de mes

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

Dado que todos los meses comienzan con '01' Puedo modificar la consulta anterior para ajustar la condición de arranque.

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

Ahora el problema viene con la fecha de finalización. Tengo que calcular manualmente la última fecha para el mes dado, tomando todos los factores en cuenta como la duración del mes, año bisiesto, etc., ya que la consulta falla si la fecha dada no es válida. Así que actualmente estoy haciendo algo como esto:

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

me gustaría saber si hay alguna manera de evitar este cálculo fecha de finalización válida?

Aclaración

he pensado por encima del primer día del siguiente mes, pero aún así voy a tener que aplicar un poco de lógica decir, si es de diciembre, el próximo mes sería de enero del próximo año. Quería saber si una solución SQL solo es posible?

Respuesta

4

Ésta es una necesidad muy común en los entornos de información.He creado varias funciones para dar cabida a estas manipulaciones fecha

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; 

continuación, puede utilizar ...

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

Es bueno evitar BETWEEN para las comparaciones de rangos de fechas. Mejor uso >= y < ya que funciona de manera similar con columnas/valores de fecha y fecha y hora.

Una forma (si se puede construir las fechas externamente):

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

También es posible usar la aritmética de fechas:

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

o la OVERLAPS operador:

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

Usted también debe leer la documentación de Postgres: Date/Time Functions and Operators

El manual explains here qué OVERLAPS funciona de esta manera:

se considera cada período de tiempo para representar el intervalo semiabierto comenzar < = tiempo < final, a menos de inicio y final son iguales en cuyo caso se representa ese instante único. Esto significa, por ejemplo, que dos períodos de tiempo con solo un punto final en común no se superponen.

+0

superposiciones funciona bien cuando sé que la última fecha. Estoy atascado cuando el último mes es diciembre. –

+0

¿De dónde obtienes los parámetros? ¿Una aplicación? ¿La web? ¿Otra pregunta? ¿Los tienes como cadenas o enteros (año, mes)? –

+1

Se debe tener cuidado al agregar un 'intervalo' a una 'fecha'. El resultado es una 'marca de tiempo'. Sin embargo, funciona en el ejemplo anterior. Lanza el resultado a 'date', si es importante. Agregar un intervalo 'n mes' siempre da como resultado el mismo día del mes, independientemente de la cantidad real de días en los meses involucrados, o el día máximo disponible, cuando el mes resultante tiene menos días. Para agregar un número exacto de días, agregue un 'entero' a una' fecha', esto da como resultado una 'fecha'. –

0

Todas las respuestas anteriores proporcionan una solución de trabajo, pero es incompleta, de un modo u otro. Como estaba buscando una solución solo de SQL (sin función), estoy combinando los mejores consejos de las soluciones anteriores.

La solución ideal para mi pregunta sería:

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

EDITAR

No estoy usando la función de superposición aquí porque estoy pasando los valores de fecha predeterminada para inicio y final como 'época ' y ahora'. Si el usuario no ha especificado ningún intervalo de tiempo se convierte en la consulta:

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

función de superposición no puede manejar 'época' y 'ahora' y da y error SQL mientras que el código anterior trabajo perfectamente para los dos casos.

PD: He votado a favor y en contra de todas las respuestas correctas, y esto me ha llevado a esta solución.

+0

Creo que debe eliminar la parte '- interval '1 day''. Pruébalo como está, con una fecha de '' 2013-04-30'' sobre la mesa. –

+0

Estoy buscando datos mensuales. Supongamos que necesito obtener todos los datos de un año, mes a aaaa-mm. Mi fecha de finalización es siempre aaaa-mm-01 a la que agrego un mes y restamos un día. –

+1

Simplemente pruebe la consulta: 'SELECT * FROM table WHERE (fecha, fecha) OVERLAPS ('2012-01-01', fecha ('2013-04-01') + intervalo '1 mes' - intervalo '1 día') 'cuando hay una' fecha' en la tabla con el valor '2013-04-30'. ¿Se devuelve? –

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 
     ; 

RESULTADO:

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) 
Cuestiones relacionadas