Szukam sposobu na zaimplementowanie funkcji Datediff SQLServer w PostgreSQL. To jest,
Ta funkcja zwraca liczbę (jako wartość całkowitą ze znakiem) określonych granic części daty, które zostały przekroczone między określoną datą początkową i końcową.
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
Wiem, że mógłbym zrobić „dd” po prostu używając odejmowania, ale masz jakieś pojęcie o pozostałych dwóch?
postgresql
date
gefei
źródło
źródło
Odpowiedzi:
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;
W ten sposób otrzymasz pełne lata, miesiąc, dni ... między dwiema datami:
age | years | months | days -----------------------+-------+--------+------ 1 year 11 mons 4 days | 1 | 11 | 4
Bardziej szczegółowe informacje o datach .
źródło
select date_part('month', age('2010-04-01', '2012-03-05'));
daje-11
. To nie jest poprawna różnica w miesiącachSELECT date_part('day', age('2016-10-05', '2015-10-02'))
zwroty3
age()
zawsze będzie złe przez lata i miesiące. Czas między2014-12-31
i2015-01-01
temu da 0 dla miesiąca i roku, adatediff()
da 1 i 1. Nie możesz po prostu dodać jedynki doage()
wyniku, ponieważage()+1
da 1 między2015-01-01
a2015-01-02
, adatediff()
teraz daje 0.Po prostu je odejmij:
SELECT ('2015-01-12'::date - '2015-01-01'::date) AS days;
Wynik:
days ------ 11
źródło
range_end - range_start ASC, id DESC
interval
, którego nie można po prostu rzutować jakoint
. Jak przekonwertować interwał na liczbę godzin za pomocą postgresów? . Użycie kombinacji funkcjidate_part
/age
, jak wspomniano w odpowiedzi @IgorRomanchenko, zwróci typdouble precision
date_part
/age
poda dni jako różnicę w części dni w dwóch datach.date_part('day', age('2016-9-05', '2015-10-02'))
zwraca 3.Spędziłem trochę czasu na poszukiwaniu najlepszej odpowiedzi i myślę, że ją mam.
Ten sql poda liczbę dni między dwiema datami jako
integer
:SELECT (EXTRACT(epoch from age('2017-6-15', now())) / 86400)::int
.. który po uruchomieniu dzisiaj (
2017-3-28
) zapewnia mi:?column? ------------ 77
Błędne przekonanie o zaakceptowanej odpowiedzi:
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'));
..jest to, że otrzymasz dosłowną różnicę między częściami ciągów dat, a nie okres między dwiema datami.
TO ZNACZY:
Age(interval)=-1 years -11 mons -4 days;
Years(double precision)=-1;
Months(double precision)=-11;
Days(double precision)=-4;
źródło
Prawie taka sama funkcja, jakiej potrzebujesz (na podstawie odpowiedzi atiruza, skrócona wersja UDF stąd )
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; $$;
Stosowanie:
/* 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 */
źródło
DATEDIFF()
Funkcja MS SQL wraca1
dodatediff(year, '2015-02-14', '2016-01-03')
. Dzieje się tak, ponieważ musisz raz przekroczyć granicę roku między tymi datami: 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
Wynik: 6
źródło
Odpowiedź @WebWanderer jest bardzo zbliżona do DateDiff przy użyciu serwera SQL, ale jest niedokładna. Dzieje się tak z powodu użycia funkcji age ().
np. dni między „2019-07-29” a „2020-06-25” powinny zwrócić 332, jednak użycie funkcji age () zwróci 327. Ponieważ funkcja age () zwraca „10 mons 27 dni” i traktuje co miesiąc jako 30 dni, co jest niepoprawne.
Aby uzyskać dokładny wynik, należy użyć sygnatury czasowej. na przykład
ceil((select extract(epoch from (current_date::timestamp - <your_date>::timestamp)) / 86400))
źródło
To pytanie jest pełne nieporozumień. Najpierw w pełni zrozumiemy pytanie. Pytający chce uzyskać taki sam efekt, jak w przypadku, gdy uruchomienie funkcji serwera MS SQL
DATEDIFF ( datepart , startdate , enddate )
, gdziedatepart
zaczynadd
,mm
alboyy
.Tę funkcję definiują:
Oznacza to, ile granic dni, miesięcy lub lat zostało przekroczonych. Nie ile dni, miesięcy czy lat jest między nimi. Dlatego jest
datediff(yy, '2010-04-01', '2012-03-05')
to 2, a nie 1. Pomiędzy tymi datami są mniej niż 2 lata, co oznacza, że minął tylko 1 rok, ale przekroczyły 2 lata, od 2010 do 2011 i od 2011 do 2012.Oto moja najlepsza próba poprawnego odtworzenia logiki.
-- 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
źródło
Chciałbym rozwinąć odpowiedź Riki_tiki_tavi i pobrać tam dane. Stworzyłem funkcję Datediff, która robi prawie wszystko, co robi serwer sql. W ten sposób możemy wziąć pod uwagę dowolną jednostkę.
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; $$;
źródło
Oto kompletny przykład z danymi wyjściowymi. psql (10.1, serwer 9.5.10).
Otrzymujesz 58, nie mniej niż 30.
Usuń funkcję age (), rozwiązując problem, o którym wspominał poprzedni post.
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
źródło
Jeszcze jedno rozwiązanie, wersja na różnicę lat:
SELECT count(*) - 1 FROM (SELECT distinct(date_trunc('year', generate_series('2010-04-01'::timestamp, '2012-03-05', '1 week')))) x
(1 rząd)
I ta sama sztuczka na miesiące:
SELECT count(*) - 1 FROM (SELECT distinct(date_trunc('month', generate_series('2010-04-01'::timestamp, '2012-03-05', '1 week')))) x
(1 rząd)
W prawdziwym zapytaniu mogą występować sekwencje znaczników czasu pogrupowane według godziny / dnia / tygodnia / itp. Zamiast generowania_serii.
To
'count(distinct(date_trunc('month', ts)))'
może być stosowany bezpośrednio w „left” stronie wybierz:SELECT sum(a - b)/count(distinct(date_trunc('month', c))) FROM d
Użyłem tutaj wygeneruj_series () tylko dla zwięzłości.
źródło