Я ищу способ реализовать SQLServer-функцию dateiff в PostgreSQL. То есть,
Эта функция возвращает количество (в виде целого числа со знаком) указанных границ datepart, пересеченных между указанными startdate и enddate.
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
Я знаю, что могу сделать 'dd', просто используя вычитание, но есть ли идеи о двух других?
postgresql
date
Gefei
источник
источник
Ответы:
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;
Это даст вам полные годы, месяц, дни ... между двумя датами:
age | years | months | days -----------------------+-------+--------+------ 1 year 11 mons 4 days | 1 | 11 | 4
Более подробная информация о датировке .
источник
select date_part('month', age('2010-04-01', '2012-03-05'));
дает-11
. Это неправильная разница в месяцахSELECT date_part('day', age('2016-10-05', '2015-10-02'))
возврат3
age()
всегда будет неправильным делать это годами и месяцами. Между2014-12-31
и2015-01-01
назад даст 0 для месяца и года, тогда какdatediff()
даст 1 и 1. Вы не можете просто прибавить единицу кage()
результату, потомуage()+1
что даст 1, когда между2015-01-01
и2015-01-02
, тогда какdatediff()
теперь дает 0.Просто вычтите их:
SELECT ('2015-01-12'::date - '2015-01-01'::date) AS days;
Результат:
days ------ 11
источник
range_end - range_start ASC, id DESC
interval
, что этот метод возвращает тип , который нельзя просто преобразовать какint
. Как мне преобразовать интервал в количество часов с помощью postgres? . Использование комбоdate_part
/age
function, как указано в ответе @IgorRomanchenko, вернет типdouble precision
date_part
/age
, как принято, предоставит дни как разницу в днях, составляющих две даты.date_part('day', age('2016-9-05', '2015-10-02'))
возвращает 3.Я потратил некоторое время на поиск лучшего ответа, и я думаю, что он у меня есть.
Этот sql даст вам количество дней между двумя датами как
integer
:SELECT (EXTRACT(epoch from age('2017-6-15', now())) / 86400)::int
..который при запуске today (
2017-3-28
) предоставляет мне:?column? ------------ 77
Заблуждение о принятом ответе:
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'));
... вы получите буквальную разницу между частями строк даты, а не количество времени между двумя датами.
IE:
Age(interval)=-1 years -11 mons -4 days;
Years(double precision)=-1;
Months(double precision)=-11;
Days(double precision)=-4;
источник
Почти та же функция, что и вам (на основе ответа атируза, сокращенная версия UDF отсюда )
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; $$;
Применение:
/* 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 */
источник
DATEDIFF()
Функции в MS SQL возвращает1
дляdatediff(year, '2015-02-14', '2016-01-03')
. Это связано с тем, что вы должны один раз пройти границу года между этими датами: 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
Результат: 6
источник
Этот вопрос полон недоразумений. Сначала давайте полностью поймем вопрос. Запрашивающий хочет получить тот же результат, что и при запуске функции MS SQL Server,
DATEDIFF ( datepart , startdate , enddate )
гдеdatepart
принимаетdd
,mm
илиyy
.Эта функция определяется:
Это означает, сколько дней, месяцев или лет пересекаются. Не сколько дней, месяцев или лет между ними. Поэтому
datediff(yy, '2010-04-01', '2012-03-05')
2, а не 1. Между этими датами меньше 2 лет, то есть прошел только 1 полный год, но пересеклись 2-летние границы: с 2010 по 2011 и с 2011 по 2012.Следующее - моя лучшая попытка правильно воспроизвести логику.
-- 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
источник
Я хотел бы расширить ответ Riki_tiki_tavi и получить данные оттуда. Я создал функцию dateiff, которая делает почти все, что делает sql server. Таким образом, мы можем учесть любую единицу.
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; $$;
источник
Ответ @WebWanderer очень близок к DateDiff с использованием SQL-сервера, но неточен. Это из-за использования функции age ().
например, дней между '2019-07-29' и '2020-06-25' должно возвращать 332, однако, используя функцию age (), она вернет 327. Поскольку age () возвращает '10 mons 27 days "и обрабатывает каждый месяц как 30 дней, что неверно.
Вы должны использовать временную метку, чтобы получить точный результат. например
ceil((select extract(epoch from (current_date::timestamp - <your_date>::timestamp)) / 86400))
источник
Вот полный пример с выводом. psql (10.1, сервер 9.5.10).
Вы получите 58, а не какое-то значение меньше 30.
Функция удаления age () решила проблему, упомянутую в предыдущем посте.
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
источник
Еще одно решение, версия для разницы в "годах":
SELECT count(*) - 1 FROM (SELECT distinct(date_trunc('year', generate_series('2010-04-01'::timestamp, '2012-03-05', '1 week')))) x
(1 ряд)
И тот же трюк на месяцы:
SELECT count(*) - 1 FROM (SELECT distinct(date_trunc('month', generate_series('2010-04-01'::timestamp, '2012-03-05', '1 week')))) x
(1 ряд)
В реальном запросе может быть несколько последовательностей временных меток, сгруппированных по часам / дням / неделям и т. Д. Вместо generate_series.
Это
'count(distinct(date_trunc('month', ts)))'
можно использовать прямо в «левой» части выбора:SELECT sum(a - b)/count(distinct(date_trunc('month', c))) FROM d
Я использовал здесь generate_series () для краткости.
источник