Estoy buscando una forma de implementar la función de SQLServer dateiff en PostgreSQL. Es decir,
Esta función devuelve el recuento (como un valor entero con signo) de los límites de la parte de fecha especificados cruzados entre la fecha de inicio y la fecha de finalización especificadas.
datediff(dd, '2010-04-01', '2012-03-05') = 704 // 704 changes of day in this interval
datediff(mm, '2010-04-01', '2012-03-05') = 23 // 23 changes of month
datediff(yy, '2010-04-01', '2012-03-05') = 2 // 2 changes of year
Sé que podría hacer 'dd' simplemente usando la resta, pero ¿alguna idea sobre los otros dos?
postgresql
date
gefei
fuente
fuente
Respuestas:
SELECT AGE('2012-03-05', '2010-04-01'), DATE_PART('year', AGE('2012-03-05', '2010-04-01')) AS years, DATE_PART('month', AGE('2012-03-05', '2010-04-01')) AS months, DATE_PART('day', AGE('2012-03-05', '2010-04-01')) AS days;
Esto te dará años completos , meses, días ... entre dos fechas:
age | years | months | days -----------------------+-------+--------+------ 1 year 11 mons 4 days | 1 | 11 | 4
Información más detallada sobre la fecha .
fuente
select date_part('month', age('2010-04-01', '2012-03-05'));
da-11
. Esa no es una diferencia correcta en mesesSELECT date_part('day', age('2016-10-05', '2015-10-02'))
devoluciones3
age()
siempre será incorrecto para hacer esto durante años y meses. Entre2014-12-31
y2015-01-01
ago dará 0 para mes y año, mientrasdatediff()
que dará 1 y 1. No se puede simplemente agregar uno alage()
resultado porqueage()+1
dará 1 cuando entre2015-01-01
y2015-01-02
, mientrasdatediff()
que ahora da 0.Simplemente réstelos:
SELECT ('2015-01-12'::date - '2015-01-01'::date) AS days;
El resultado:
days ------ 11
fuente
range_end - range_start ASC, id DESC
interval
, que no se puede convertir simplemente comoint
. ¿Cómo convierto un intervalo en varias horas con postgres? . Usando el combodate_part
/age
function, como se menciona en la respuesta de @IgorRomanchenko, se devolverá el tipodouble precision
date_part
/age
answer, según se acepta, proporcionará días como la diferencia entre los días de las dos fechas.date_part('day', age('2016-9-05', '2015-10-02'))
devuelve 3.Pasé un tiempo buscando la mejor respuesta y creo que la tengo.
Este sql le dará el número de días entre dos fechas como
integer
:SELECT (EXTRACT(epoch from age('2017-6-15', now())) / 86400)::int
..que, cuando se ejecuta hoy (
2017-3-28
), me proporciona:?column? ------------ 77
El concepto erróneo sobre la respuesta aceptada:
select age('2010-04-01', '2012-03-05'), date_part('year',age('2010-04-01', '2012-03-05')), date_part('month',age('2010-04-01', '2012-03-05')), date_part('day',age('2010-04-01', '2012-03-05'));
... es que obtendrá la diferencia literal entre las partes de las cadenas de fechas, no la cantidad de tiempo entre las dos fechas.
ES DECIR:
Age(interval)=-1 years -11 mons -4 days;
Years(double precision)=-1;
Months(double precision)=-11;
Days(double precision)=-4;
fuente
Casi la misma función que necesitaba (según la respuesta de atiruz, versión abreviada de UDF desde aquí )
CREATE OR REPLACE FUNCTION datediff(type VARCHAR, date_from DATE, date_to DATE) RETURNS INTEGER LANGUAGE plpgsql AS $$ DECLARE age INTERVAL; BEGIN CASE type WHEN 'year' THEN RETURN date_part('year', date_to) - date_part('year', date_from); WHEN 'month' THEN age := age(date_to, date_from); RETURN date_part('year', age) * 12 + date_part('month', age); ELSE RETURN (date_to - date_from)::int; END CASE; END; $$;
Uso:
/* Get months count between two dates */ SELECT datediff('month', '2015-02-14'::date, '2016-01-03'::date); /* Result: 10 */ /* Get years count between two dates */ SELECT datediff('year', '2015-02-14'::date, '2016-01-03'::date); /* Result: 1 */ /* Get days count between two dates */ SELECT datediff('day', '2015-02-14'::date, '2016-01-03'::date); /* Result: 323 */ /* Get months count between specified and current date */ SELECT datediff('month', '2015-02-14'::date, NOW()::date); /* Result: 47 */
fuente
DATEDIFF()
función en MS SQL devuelve1
adatediff(year, '2015-02-14', '2016-01-03')
. Esto se debe a que debe pasar el límite del año una vez entre esas fechas: docs.microsoft.com/en-us/sql/t-sql/functions/…SELECT date_part ('year', f) * 12 + date_part ('month', f) FROM age ('2015-06-12'::DATE, '2014-12-01'::DATE) f
Resultado: 6
fuente
Esta pregunta está llena de malentendidos. Primero, entendamos la pregunta completamente. El autor de la pregunta quiere obtener el mismo resultado que para cuando se ejecuta la función de MS SQL Server
DATEDIFF ( datepart , startdate , enddate )
, dondedatepart
tomadd
,mm
oyy
.Esta función está definida por:
Eso significa cuántos límites de días, meses o años se cruzan. No cuántos días, meses o años hay entre ellos. Es por eso que
datediff(yy, '2010-04-01', '2012-03-05')
es 2, y no 1. Hay menos de 2 años entre esas fechas, lo que significa que solo ha pasado 1 año completo, pero se han cruzado límites de 2 años, de 2010 a 2011 y de 2011 a 2012.Los siguientes son mi mejor intento de replicar la lógica correctamente.
-- datediff(dd`, '2010-04-01', '2012-03-05') = 704 // 704 changes of day in this interval select ('2012-03-05'::date - '2010-04-01'::date ); -- 704 changes of day -- datediff(mm, '2010-04-01', '2012-03-05') = 23 // 23 changes of month select (date_part('year', '2012-03-05'::date) - date_part('year', '2010-04-01'::date)) * 12 + date_part('month', '2012-03-05'::date) - date_part('month', '2010-04-01'::date) -- 23 changes of month -- datediff(yy, '2010-04-01', '2012-03-05') = 2 // 2 changes of year select date_part('year', '2012-03-05'::date) - date_part('year', '2010-04-01'::date); -- 2 changes of year
fuente
Me gustaría ampliar la respuesta de Riki_tiki_tavi y obtener los datos. He creado una función dateiff que hace casi todo lo que hace el servidor SQL. De esa forma podemos tener en cuenta cualquier unidad.
create function datediff(units character varying, start_t timestamp without time zone, end_t timestamp without time zone) returns integer language plpgsql as $$ DECLARE diff_interval INTERVAL; diff INT = 0; years_diff INT = 0; BEGIN IF units IN ('yy', 'yyyy', 'year', 'mm', 'm', 'month') THEN years_diff = DATE_PART('year', end_t) - DATE_PART('year', start_t); IF units IN ('yy', 'yyyy', 'year') THEN -- SQL Server does not count full years passed (only difference between year parts) RETURN years_diff; ELSE -- If end month is less than start month it will subtracted RETURN years_diff * 12 + (DATE_PART('month', end_t) - DATE_PART('month', start_t)); END IF; END IF; -- Minus operator returns interval 'DDD days HH:MI:SS' diff_interval = end_t - start_t; diff = diff + DATE_PART('day', diff_interval); IF units IN ('wk', 'ww', 'week') THEN diff = diff/7; RETURN diff; END IF; IF units IN ('dd', 'd', 'day') THEN RETURN diff; END IF; diff = diff * 24 + DATE_PART('hour', diff_interval); IF units IN ('hh', 'hour') THEN RETURN diff; END IF; diff = diff * 60 + DATE_PART('minute', diff_interval); IF units IN ('mi', 'n', 'minute') THEN RETURN diff; END IF; diff = diff * 60 + DATE_PART('second', diff_interval); RETURN diff; END; $$;
fuente
La respuesta de @WebWanderer es muy cercana al DateDiff usando el servidor SQL, pero inexacta. Eso se debe al uso de la función age ().
por ejemplo, los días entre '2019-07-29' y '2020-06-25' deberían devolver 332, sin embargo, al usar la función age (), devolverá 327. Porque age () devuelve '10 mons 27 días "y trata cada mes como 30 días, lo cual es incorrecto.
Debe utilizar la marca de tiempo para obtener un resultado preciso. p.ej
ceil((select extract(epoch from (current_date::timestamp - <your_date>::timestamp)) / 86400))
fuente
Aquí hay un ejemplo completo con salida. psql (10.1, servidor 9.5.10).
Obtienes 58, no un valor menor que 30.
Eliminar la función age (), resolvió el problema que mencionaba la publicación anterior.
drop table t; create table t( d1 date ); insert into t values(current_date - interval '58 day'); select d1 , current_timestamp - d1::timestamp date_diff , date_part('day', current_timestamp - d1::timestamp) from t; d1 | date_diff | date_part ------------+-------------------------+----------- 2018-05-21 | 58 days 21:41:07.992731 | 58
fuente
Una solución más, versión para la diferencia de 'años':
SELECT count(*) - 1 FROM (SELECT distinct(date_trunc('year', generate_series('2010-04-01'::timestamp, '2012-03-05', '1 week')))) x
(1 fila)
Y el mismo truco para los meses:
SELECT count(*) - 1 FROM (SELECT distinct(date_trunc('month', generate_series('2010-04-01'::timestamp, '2012-03-05', '1 week')))) x
(1 fila)
En la consulta de la vida real, puede haber algunas secuencias de marcas de tiempo agrupadas por hora / día / semana / etc.en lugar de generate_series.
Esto
'count(distinct(date_trunc('month', ts)))'
se puede usar a la derecha en el lado 'izquierdo' de la selección:SELECT sum(a - b)/count(distinct(date_trunc('month', c))) FROM d
Usé generate_series () aquí solo por brevedad.
fuente