web-dev-qa-db-fra.com

PostgreSQL: jours/mois/années entre deux dates

Je cherche un moyen d'implémenter la SQLServer-function de datiff dans PostgreSQL. C'est, 

Cette fonction renvoie le nombre (en tant que valeur entière signée) des limites de datePart spécifié spécifiées croisées entre les dates de début et de fin spécifiées.

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

Je sais que je pourrais faire 'dd' en utilisant simplement une soustraction, mais avez-vous une idée des deux autres?

58
gefei

Essayez quelque chose comme:

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'));

Ces fonctions vous donneront plein années, mois, jours ... entre deux dates. Pour le nombre de changements de date, utilisez le lien fourni par revoua: http://www.sqlines.com/postgresql/how-to/datediff

88
Igor Romanchenko

il suffit de les soustraire:

select '2015-01-12'::date - '2015-01-01'::date;

Cela vous donnera:

?column? 
----------
   11
84
mehdi

Presque la même fonction que vous aviez besoin (basé sur la réponse d’atiruz, version abrégée de UDF à partir de ici )

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;
$$;

Usage:

/* 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 */
9
Riki_tiki_tavi
SELECT date_part ('year', f) * 12
     + date_part ('month', f)
FROM age ('2015-06-12'::DATE, '2014-12-01'::DATE) f

Résultat: 6

5
atiruz

Je voudrais développer la réponse de Riki_tiki_tavi et obtenir les données. J'ai créé une fonction de date qui fait presque tout ce que le serveur SQL fait. Ainsi, nous pouvons prendre en compte n’importe quelle unité.

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;
$$;
0

Voici un exemple complet avec sortie. psql (10.1, serveur 9.5.10).

Vous obtenez 58, pas une valeur inférieure à 30.
Supprimer la fonction age (), a résolu le problème mentionné dans un précédent article.

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
0
Charlie 木匠

Cette question est pleine de malentendus. Tout d'abord, permet de bien comprendre la question. Le demandeur souhaite obtenir le même résultat que lors de l'exécution de la fonction MS SQL ServerDATEDIFF ( datepart , startdate , enddate )datepart prend dd, mm ou yy

Cette fonction est définie par:

Cette fonction renvoie le nombre (en tant que valeur entière signée) des limites de datePart spécifié spécifiées croisées entre les dates de début et de fin spécifiées.

Cela signifie combien de jours, de mois ou d’années sont franchis. Pas combien de jours, mois ou années c'est entre eux. C’est pourquoi datediff(yy, '2010-04-01', '2012-03-05') est égal à 2 et non à 1. Il y a moins de 2 ans entre ces dates, ce qui signifie qu’une année entière est écoulée, mais que deux années se sont écoulées, de 2010 à 2011 et de 2011 à 2012.

Voici ma meilleure tentative pour reproduire correctement la logique.

-- 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
0
André C. Andersen