Estou procurando uma maneira de implementar o datediff de função SQLServer no PostgreSQL. Isso é,
Esta função retorna a contagem (como um valor inteiro assinado) dos limites da parte da data especificada cruzada entre a data de início e a data de término 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
Eu sei que poderia fazer 'dd' simplesmente usando subtração, mas alguma ideia sobre os outros dois?
postgresql
date
gefei
fonte
fonte
Respostas:
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;
Isso lhe dará anos, meses, dias completos ... entre duas datas:
age | years | months | days -----------------------+-------+--------+------ 1 year 11 mons 4 days | 1 | 11 | 4
Informações mais detalhadas sobre datediff .
fonte
select date_part('month', age('2010-04-01', '2012-03-05'));
dá-11
. Essa não é uma diferença correta em mesesSELECT date_part('day', age('2016-10-05', '2015-10-02'))
retorna3
age()
sempre será errado por anos e meses. Entre2014-12-31
e2015-01-01
atrás vai dar 0 para mês e ano, enquantodatediff()
vai dar 1 e 1. Você não pode simplesmente adicionar um aoage()
resultado porqueage()+1
vai dar 1 quando entre2015-01-01
e2015-01-02
, enquantodatediff()
que agora dá 0.Basta subtraí-los:
SELECT ('2015-01-12'::date - '2015-01-01'::date) AS days;
O resultado:
days ------ 11
fonte
range_end - range_start ASC, id DESC
interval
, que não pode ser simplesmente convertido comoint
. Como faço para converter um intervalo em várias horas com o postgres? . Usando o combodate_part
/age
function, como mencionado na resposta de @IgorRomanchenko, retornará typedouble precision
date_part
/age
, conforme aceita, fornecerá dias como a diferença na parte dos dias das duas datas.date_part('day', age('2016-9-05', '2015-10-02'))
retorna 3.Passei algum tempo procurando a melhor resposta e acho que consegui.
Este sql fornecerá o número de dias entre duas datas como
integer
:SELECT (EXTRACT(epoch from age('2017-6-15', now())) / 86400)::int
..que, quando executado hoje (
2017-3-28
), me fornece:?column? ------------ 77
O equívoco sobre a resposta aceita:
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'));
..is que você obterá a diferença literal entre as partes das strings de data, não a quantidade de tempo entre as duas datas.
IE:
Age(interval)=-1 years -11 mons -4 days;
Years(double precision)=-1;
Months(double precision)=-11;
Days(double precision)=-4;
fonte
Quase a mesma função que você precisava (com base na resposta de atiruz, versão abreviada de UDF a partir daqui )
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 */
fonte
DATEDIFF()
função em MS SQL retorna1
paradatediff(year, '2015-02-14', '2016-01-03')
. Isso ocorre porque você deve ultrapassar o limite do ano uma vez entre essas datas: 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
fonte
A resposta de @WebWanderer é muito próxima do DateDiff usando o servidor SQL, mas imprecisa. Isso se deve ao uso da função age ().
por exemplo, dias entre '2019-07-29' e '2020-06-25' devem retornar 332, no entanto, usando a função age () ele retornará 327. Porque age () retorna '10 mons 27 dias "e trata cada mês como 30 dias, o que está incorreto.
Você deve usar o carimbo de data / hora para obter o resultado preciso. por exemplo
ceil((select extract(epoch from (current_date::timestamp - <your_date>::timestamp)) / 86400))
fonte
Esta questão está cheia de mal-entendidos. Primeiro, vamos entender a questão completamente. O autor da questão quer obter o mesmo resultado que quando executando a função de MS SQL Server
DATEDIFF ( datepart , startdate , enddate )
, ondedatepart
levadd
,mm
ouyy
.Esta função é definida por:
Isso significa quantos limites de dias, limites de meses ou limites de anos foram ultrapassados. Não quantos dias, meses ou anos entre eles. É por isso que
datediff(yy, '2010-04-01', '2012-03-05')
é 2, e não 1. Há menos de 2 anos entre essas datas, o que significa que apenas 1 ano inteiro se passou, mas os limites de 2 anos se cruzaram, de 2010 a 2011 e de 2011 a 2012.A seguir, minha melhor tentativa de replicar a lógica corretamente.
-- 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
fonte
Eu gostaria de expandir a resposta de Riki_tiki_tavi e divulgar os dados. Eu criei uma função datediff que faz quase tudo que o sql server faz. Assim podemos levar em consideração qualquer unidade.
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; $$;
fonte
Aqui está um exemplo completo com saída. psql (10.1, servidor 9.5.10).
Você obtém 58, não algum valor menor que 30.
Remova a função age (), resolveu o problema que o post anterior mencionou.
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
fonte
Mais uma solução, versão para a diferença de 'anos':
SELECT count(*) - 1 FROM (SELECT distinct(date_trunc('year', generate_series('2010-04-01'::timestamp, '2012-03-05', '1 week')))) x
(1 linha)
E o mesmo truque para os meses:
SELECT count(*) - 1 FROM (SELECT distinct(date_trunc('month', generate_series('2010-04-01'::timestamp, '2012-03-05', '1 week')))) x
(1 linha)
Na consulta da vida real, pode haver algumas sequências de carimbo de data / hora agrupadas por hora / dia / semana / etc em vez de generate_series.
Isso
'count(distinct(date_trunc('month', ts)))'
pode ser usado à direita no lado 'esquerdo' do select:SELECT sum(a - b)/count(distinct(date_trunc('month', c))) FROM d
Usei generate_series () aqui apenas por uma questão de brevidade.
fonte