Jak obliczyć wiek (w latach) na podstawie daty urodzenia i getDate ()


171

Mam tabelę z listą osób wraz z ich datą urodzenia (obecnie nvarchar (25))

Jak mogę przekształcić to w datę, a następnie obliczyć ich wiek w latach?

Moje dane wyglądają następująco

ID    Name   DOB
1     John   1992-01-09 00:00:00
2     Sally  1959-05-20 00:00:00

Chciałbym zobaczyć:

ID    Name   AGE  DOB
1     John   17   1992-01-09 00:00:00
2     Sally  50   1959-05-20 00:00:00

16
Dlaczego przechowujesz wartości dat jako ciągi przy użyciu nvarchar (25) zamiast używania natywnej daty lub typu datetime bazy danych?
Jesper

Pytanie jest oznaczone jako 2005, a nie 2008, więc rodzimy typ „Date” nie jest dostępny, ale na pewno jest datą i godziną, i można argumentować, że jest to SmallDateTime, ponieważ nie potrzebujesz dokładności.
Andrew,

Cześć, powodem utrzymywania dat jako varchar jest to, że importuję to ze schematu serwera innego niż SQL, wystąpiły problemy z importowaniem ich jako datetime (i innych formatów daty) i varchar przekonwertowany ok
Jimmy

7
@ James.Elsey, więc miałeś problemy z importowaniem i czy w rezultacie wszystkie daty są prawidłowe? Nigdy nie możesz być pewien, chyba że używasz datetime lub smalldatetime, z varchar, możesz sprawić, że import zadziała, ale będziesz mieć inne problemy. Poza tym nigdy nie przechowywałbym wieku, zmienia się każdego dnia, używam View
KM.

@KM Tak, wystąpił problem podczas importowania tych danych jako daty, jedynym realnym rozwiązaniem w tamtym czasie było zaimportowanie ich jako nvarchars. Ten wybór będzie częścią codziennej pracy, więc przechowywanie wieku nie powinno stanowić problemu
Jimmy

Odpowiedzi:


256

Występują problemy z rokiem / dniami przestępnymi i następującą metodą, zobacz aktualizację poniżej:

Spróbuj tego:

DECLARE @dob  datetime
SET @dob='1992-01-09 00:00:00'

SELECT DATEDIFF(hour,@dob,GETDATE())/8766.0 AS AgeYearsDecimal
    ,CONVERT(int,ROUND(DATEDIFF(hour,@dob,GETDATE())/8766.0,0)) AS AgeYearsIntRound
    ,DATEDIFF(hour,@dob,GETDATE())/8766 AS AgeYearsIntTrunc

WYNIK:

AgeYearsDecimal                         AgeYearsIntRound AgeYearsIntTrunc
--------------------------------------- ---------------- ----------------
17.767054                               18               17

(1 row(s) affected)

AKTUALIZUJ tutaj kilka dokładniejszych metod:

NAJLEPSZA METODA NA LATA W INT

DECLARE @Now  datetime, @Dob datetime
SELECT   @Now='1990-05-05', @Dob='1980-05-05'  --results in 10
--SELECT @Now='1990-05-04', @Dob='1980-05-05'  --results in  9
--SELECT @Now='1989-05-06', @Dob='1980-05-05'  --results in  9
--SELECT @Now='1990-05-06', @Dob='1980-05-05'  --results in 10
--SELECT @Now='1990-12-06', @Dob='1980-05-05'  --results in 10
--SELECT @Now='1991-05-04', @Dob='1980-05-05'  --results in 10

SELECT
    (CONVERT(int,CONVERT(char(8),@Now,112))-CONVERT(char(8),@Dob,112))/10000 AS AgeIntYears

możesz zmienić powyższe 10000na 10000.0i uzyskać ułamki dziesiętne, ale nie będzie to tak dokładne, jak metoda poniżej.

NAJLEPSZA METODA LAT W UDZIALE UMIESZCZONYM

DECLARE @Now  datetime, @Dob datetime
SELECT   @Now='1990-05-05', @Dob='1980-05-05' --results in 10.000000000000
--SELECT @Now='1990-05-04', @Dob='1980-05-05' --results in  9.997260273973
--SELECT @Now='1989-05-06', @Dob='1980-05-05' --results in  9.002739726027
--SELECT @Now='1990-05-06', @Dob='1980-05-05' --results in 10.002739726027
--SELECT @Now='1990-12-06', @Dob='1980-05-05' --results in 10.589041095890
--SELECT @Now='1991-05-04', @Dob='1980-05-05' --results in 10.997260273973

SELECT 1.0* DateDiff(yy,@Dob,@Now) 
    +CASE 
         WHEN @Now >= DATEFROMPARTS(DATEPART(yyyy,@Now),DATEPART(m,@Dob),DATEPART(d,@Dob)) THEN  --birthday has happened for the @now year, so add some portion onto the year difference
           (  1.0   --force automatic conversions from int to decimal
              * DATEDIFF(day,DATEFROMPARTS(DATEPART(yyyy,@Now),DATEPART(m,@Dob),DATEPART(d,@Dob)),@Now) --number of days difference between the @Now year birthday and the @Now day
              / DATEDIFF(day,DATEFROMPARTS(DATEPART(yyyy,@Now),1,1),DATEFROMPARTS(DATEPART(yyyy,@Now)+1,1,1)) --number of days in the @Now year
           )
         ELSE  --birthday has not been reached for the last year, so remove some portion of the year difference
           -1 --remove this fractional difference onto the age
           * (  -1.0   --force automatic conversions from int to decimal
                * DATEDIFF(day,DATEFROMPARTS(DATEPART(yyyy,@Now),DATEPART(m,@Dob),DATEPART(d,@Dob)),@Now) --number of days difference between the @Now year birthday and the @Now day
                / DATEDIFF(day,DATEFROMPARTS(DATEPART(yyyy,@Now),1,1),DATEFROMPARTS(DATEPART(yyyy,@Now)+1,1,1)) --number of days in the @Now year
             )
     END AS AgeYearsDecimal

24
To też nie jest dokładne rozwiązanie. Jeśli wezmę własne @dob jako `` 1986-07-05 00:00:00 '' i wykonam to (użyj innej zmiennej zamiast GETDATE()) w dniu `` 2013-07-04 23:59:59 '', to mówi: I Mam 27 lat, podczas gdy w tej chwili jeszcze nie jestem. Przykładowy kod: declare @startDate nvarchar(100) = '1986-07-05 00:00:00' declare @endDate nvarchar(100) = '2013-07-04 23:59:59' SELECT DATEDIFF(hour,@startDate,@endDate)/8766.0 AS AgeYearsDecimal ,CONVERT(int,ROUND(DATEDIFF(hour,@startDate,@endDate)/8766.0,0)) AS AgeYearsIntRound ,DATEDIFF(hour,@startDate,@endDate)/8766 AS AgeYearsIntTrunc
bartlaarhoven

20
Nie jest to dokładne, ponieważ zakłada 8766 godzin rocznie, co daje 365,25 dni. Ponieważ nie ma lat z 365,25 dniami, częściej będzie to nieprawidłowe w pobliżu daty urodzenia danej osoby niż jest to poprawne. Ta metoda będzie nadal dokładniejsza.
Bacon Bits

1
Drugi komentarz @Bacon Bits - często będzie to błąd, gdy aktualna data jest bliska dacie urodzenia danej osoby.
flash

2
Myślę, że pierwszy blok tekstu sprawia, że ​​ta odpowiedź jest zagmatwana. Jeśli w zaktualizowanej metodzie nie występuje problem z latami przestępnymi, proponuję (jeśli naprawdę chcesz ją w ogóle zachować), aby przesunąć ją na dół odpowiedzi.
ajbeaven

1
Jeśli chcesz EXACT obliczenia, następnie DateDiff nie zrobi @ShailendraMishra, ponieważ zbliża dni, itp. Na przykład select datediff(year, '2000-01-05', '2018-01-04')zwraca 18, a nie 17, jak powinno. Skorzystałem z powyższego przykładu pod nagłówkiem „NAJLEPSZA METODA NA LATA W INT” i działa idealnie. Dzięki!
openwonk

132

Muszę to wyrzucić. Jeśli przekonwertujesz datę za pomocą stylu 112 (rrrrmmdd) na liczbę, możesz użyć takiego obliczenia ...

(rrrrMMdd - rrrrMMdd) / 10000 = różnica w pełnych latach

declare @as_of datetime, @bday datetime;
select @as_of = '2009/10/15', @bday = '1980/4/20'

select 
    Convert(Char(8),@as_of,112),
    Convert(Char(8),@bday,112),
    0 + Convert(Char(8),@as_of,112) - Convert(Char(8),@bday,112), 
    (0 + Convert(Char(8),@as_of,112) - Convert(Char(8),@bday,112)) / 10000

wynik

20091015    19800420    290595  29

14
Jest to niemal magiczne, ponieważ rozwiązuje wszystkie problemy związane z rokiem przestępnym. Warto zauważyć, że 112 to specjalna liczba funkcji CONVERT, która formatuje datę jako rrrrmmdd. Prawdopodobnie nie jest to oczywiste dla wszystkich, kiedy na to spojrzysz.
Derek Tomes

5
Jesteś geniuszem!
ercan

Mój zespół napotkał problem, gdy data, której używaliśmy do znalezienia wieku, była tego samego dnia, co data, z którą ją porównujemy. Zauważyliśmy, że kiedy były tego samego dnia (i jeśli wiek miał być dziwny), wiek byłby niższy o jeden. To działało idealnie!
The Sheek Geek

1
To jest prawidłowa odpowiedź - zaznacz ją jako taką.
Snympi

4
Najprostszy / najkrótszy kod dla tej samej metody obliczania w SQL Server 2012+ tocode: SELECT [Age] = (0+ FORMAT(@as_of,'yyyyMMdd') - FORMAT(@bday,'yyyyMMdd') ) /10000 --The 0+ part tells SQL to calc the char(8) as numbers
ukgav

44

Używam tego zapytania w naszym kodzie produkcyjnym przez prawie 10 lat:

SELECT FLOOR((CAST (GetDate() AS INTEGER) - CAST(Date_of_birth AS INTEGER)) / 365.25) AS Age

6
To nie jest złe, ale nie jest 100%, 2007/10/16 zgłosi wiek 2 w 2009/10/15
Andrew

4
Doh, brakuje nam oczywistego, jest już po południu, getdate zwraca int, więc oczywiście będzie zaokrąglany w górę. Skopiowałem, wkleiłem Twoją odpowiedź i uruchomiłem ją, więc automatycznie użyłem getdate, a nie dosłownego.
Andrew,

2
Eleganckie i proste. Dzięki!
William MB

9
Jeśli mówimy o wieku ludzi, powinieneś obliczyć go tak, jak ludzie obliczają wiek. Nie ma to nic wspólnego z szybkością ruchu ziemi ani z kalendarzem. Za każdym razem, gdy upłynie ten sam miesiąc i dzień co data urodzenia, zwiększasz wiek o 1. Oznacza to, że poniższe informacje są najbardziej dokładne, ponieważ odzwierciedlają to, co ludzie mają na myśli, kiedy mówią „wiek”:DATEDIFF(yy, @BirthDate, GETDATE()) - CASE WHEN (MONTH(@BirthDate) >= MONTH(GETDATE())) AND DAY(@BirthDate) > DAY(GETDATE()) THEN 1 ELSE 0 END
Bacon Bits.

5
Przepraszamy, ta składnia jest nieprawidłowa. CASE WHEN (MONTH(@date) > MONTH(GETDATE())) OR (MONTH(@date) = MONTH(GETDATE()) AND DAY(@date) > DAY(GETDATE())) THEN 1 ELSE 0 END
Bacon Bits

31

Tak wiele z powyższych rozwiązań jest błędnych DateDiff (yy, @ Dob, @PassedDate) nie uwzględni miesiąca i dnia obu dat. Również branie części do rzutek i porównywanie działa tylko wtedy, gdy są odpowiednio zamówione.

NASTĘPUJĄCY KOD DZIAŁA I JEST BARDZO PROSTY:

create function [dbo].[AgeAtDate](
    @DOB    datetime,
    @PassedDate datetime
)

returns int
with SCHEMABINDING
as
begin

declare @iMonthDayDob int
declare @iMonthDayPassedDate int


select @iMonthDayDob = CAST(datepart (mm,@DOB) * 100 + datepart  (dd,@DOB) AS int) 
select @iMonthDayPassedDate = CAST(datepart (mm,@PassedDate) * 100 + datepart  (dd,@PassedDate) AS int) 

return DateDiff(yy,@DOB, @PassedDate) 
- CASE WHEN @iMonthDayDob <= @iMonthDayPassedDate
  THEN 0 
  ELSE 1
  END

End

Dlaczego mnożysz przez 100? To działa dla mnie, ponieważ próbuję replikować w bazie danych to, co istnieje w naszej bibliotece kodu - ale nie mogłem wyjaśnić Twojej funkcji. To może być głupie pytanie :)
Jen

6
Dzięki! Dokładnie kod, którego się tutaj spodziewałem. To jedyny dokładnie poprawny kod w tym wątku bez (brzydkich) przekształceń łańcuchów! @Jen Zajmuje miesiąc i dzień daty urodzenia (np. 25 września) i zamienia je na liczbę całkowitą 0925(lub 925). Robi to samo z bieżącą datą (na przykład 16 grudnia staje się 1216), a następnie sprawdza, czy wartość całkowita DoB już przeszła. Aby utworzyć tę liczbę całkowitą, należy pomnożyć miesiąc przez 100.
bartlaarhoven

Dzięki @bartlaarhoven :)
Jen

Wspomnę tylko, że chociaż pozwala to uniknąć transformacji ciągów, zamiast tego wykonuje dużo rzutowania. Moje testy pokazują, że nie jest to znacznie szybsze niż odpowiedź dotjoe , a kod jest bardziej szczegółowy.
StriplingWarrior

zaakceptowana odpowiedź ma znacznie prostszą odpowiedź INT:(CONVERT(int,CONVERT(char(8),@Now,112))-CONVERT(char(8),@Dob,112))/10000
KM.

19

Musisz wziąć pod uwagę sposób zaokrągleń komendy datediff.

SELECT CASE WHEN dateadd(year, datediff (year, DOB, getdate()), DOB) > getdate()
            THEN datediff(year, DOB, getdate()) - 1
            ELSE datediff(year, DOB, getdate())
       END as Age
FROM <table>

Które zaadaptowałem stąd .

Należy pamiętać, że data 28 lutego będzie traktowana jako data urodzin skoczka w przypadku lat innych niż przestępne, np. Osoba urodzona 29 lutego 2020 r. Będzie uważana za roczną 28 lutego 2021 r. Zamiast 1 marca 2021 r.


@Andrew - Poprawione - przegapiłem jedną ze zmian
Ed Harper

1
Wersja uproszczonaSELECT DATEDIFF(year, DOB, getdate()) + CASE WHEN (DATEADD(year,DATEDIFF(year, DOB, getdate()) , DOB) > getdate()) THEN - 1 ELSE 0 END)
Peter

To jest właściwe podejście; Nie rozumiem, dlaczego tak bardzo popiera się hacków.
Salman A

8

EDYCJA: TA ODPOWIEDŹ JEST NIEPRAWIDŁOWA. Zostawiam to tutaj jako ostrzeżenie dla każdego, kto ma ochotę użyć dayofyear, z dalszą edycją na końcu.


Jeśli, tak jak ja, nie chcesz dzielić przez ułamkowe dni lub ryzykować błędów zaokrągleń / lat przestępnych, pochwalam komentarz @Bacon Bits w poście nad https://stackoverflow.com/a/1572257/489865, gdzie mówi:

Jeśli mówimy o wieku ludzi, powinieneś obliczyć go tak, jak ludzie obliczają wiek. Nie ma to nic wspólnego z szybkością ruchu ziemi ani z kalendarzem. Za każdym razem, gdy upłynie ten sam miesiąc i dzień co data urodzenia, zwiększasz wiek o 1. Oznacza to, że poniższe informacje są najdokładniejsze, ponieważ odzwierciedlają to, co ludzie mają na myśli, gdy mówią „wiek”.

Następnie oferuje:

DATEDIFF(yy, @date, GETDATE()) -
CASE WHEN (MONTH(@date) > MONTH(GETDATE())) OR (MONTH(@date) = MONTH(GETDATE()) AND DAY(@date) > DAY(GETDATE()))
THEN 1 ELSE 0 END

Jest tu kilka sugestii dotyczących porównania miesiąca i dnia (a niektórzy mylą się, nie dopuszczając, aby ORtak było poprawnie tutaj!). Ale nikt nie zaoferował dayofyear, co wydaje się tak proste i znacznie krótsze. Proponuję:

DATEDIFF(year, @date, GETDATE()) -
CASE WHEN DATEPART(dayofyear, @date) > DATEPART(dayofyear, GETDATE()) THEN 1 ELSE 0 END

[Uwaga: nigdzie w SQL BOL / MSDN DATEPART(dayofyear, ...)zwroty nie są faktycznie udokumentowane! Rozumiem, że jest to liczba z zakresu 1--366; co najważniejsze, nie zmienia się według ustawień regionalnych zgodnie z DATEPART(weekday, ...)& SET DATEFIRST.]


EDIT: Dlaczego dayofyearidzie źle : Jako użytkownik @AeroX skomentował, czy narodziny / data rozpoczęcia jest po lutego w roku przestępnym nie wiek jest o jeden dzień wcześniej, gdy bieżąca data zakończenia / jest rokiem przestępnym, na przykład '2015-05-26', '2016-05-25'daje wiek 1, kiedy powinno nadal wynosić 0. Porównywanie lat dayofyearw różnych latach jest wyraźnie niebezpieczne. Więc używanie MONTH()i DAY()jest w końcu konieczne.


To powinno zostać przegłosowane lub nawet oznaczone jako odpowiedź. Jest krótki, elegancki i logicznie poprawny.
z00l

1
W przypadku wszystkich urodzonych po lutym ich Wiek jest zwiększany o jeden dzień wcześniej w każdym roku przestępnym przy użyciu tej DayOfYearmetody.
AeroX

4
@AeroX Dziękuję za dostrzeżenie tej usterki. Postanowiłem zostawić moje rozwiązanie jako ostrzeżenie dla każdego, kto mógł lub używał dayofyear, ale zostało wyraźnie zredagowane, aby pokazać, dlaczego idzie źle. Mam nadzieję, że to jest odpowiednie.
JonBrave

5

Ponieważ nie ma jednej prostej odpowiedzi, która zawsze podaje właściwy wiek, oto, co wymyśliłem.

SELECT DATEDIFF(YY, DateOfBirth, GETDATE()) - 
     CASE WHEN RIGHT(CONVERT(VARCHAR(6), GETDATE(), 12), 4) >= 
               RIGHT(CONVERT(VARCHAR(6), DateOfBirth, 12), 4) 
     THEN 0 ELSE 1 END AS AGE 

W ten sposób uzyskuje się roczną różnicę między datą urodzenia a datą bieżącą. Następnie odejmuje rok, jeśli data urodzenia jeszcze nie minęła.

Dokładne przez cały czas - niezależnie od lat przestępnych lub tego, jak blisko daty urodzenia.

A co najlepsze - brak funkcji.


3
SELECT ID,
Name,
DATEDIFF(yy,CONVERT(DATETIME, DOB),GETDATE()) AS AGE,
DOB
FROM MyTable

1
Chcesz getdate jako drugi argument, a nie pierwszy, w przeciwnym razie otrzymasz wyniki liczb ujemnych i rundy datediff, więc wybierz datediff (yy, '20081231', getdate ()) zgłosi wiek 1, ale będzie miał tylko 10 miesięcy .
Andrew

1
To wyliczenie daje nieprawidłowe obliczenia dla osób, które w tym roku nie miały jeszcze swoich urodzin.

3

Co powiesz na:

DECLARE @DOB datetime
SET @DOB='19851125'   
SELECT Datepart(yy,convert(date,GETDATE())-@DOB)-1900

Czy nie pozwoliłoby to uniknąć tych wszystkich zaokrągleń, obcinania i rozwiązywania problemów?


Twoje obliczenia nie są dokładne. Na przykład: zawodzi, jeśli bierzesz '1986-07-05 00:00:00'za DOB i '2013-07-04 23:59:59'na bieżący czas.
drinovc

@ub_coding Czy oferujesz i odpowiadasz lub zadajesz inne pytanie?
Aaron C

to: DECLARE @DOB datetime SET @ DOB = '19760229' SELECT Datepart (yy, convert (datetime, '19770228') - @ DOB) -1900 = 1 głównym problemem jest fev 29 luka dla większości rozwiązań, to wyjaśnia obcięcie zaokrąglenia itp. .
Leonardo Marques de Souza

3

Wydaje mi się, że jest to podobne do innych zamieszczonych tutaj .... ale to rozwiązanie działało w przykładach z lat przestępnych od 02/29/1976 do 03/01/2011, a także działało w przypadku w pierwszym roku .. jak 07/04 / 2011 do 07/03/2012, który ostatni opublikował o rozwiązaniu roku przestępnego, nie działał w przypadku użycia w pierwszym roku.

SELECT FLOOR(DATEDIFF(DAY, @date1 , @date2) / 365.25)

Znaleziono tutaj .


3

Po prostu sprawdź, czy poniższa odpowiedź jest możliwa.

DECLARE @BirthDate DATE = '09/06/1979'

SELECT 
 (
 YEAR(GETDATE()) - YEAR(@BirthDate) - 
 CASE  WHEN (MONTH(GETDATE()) * 100) + DATEPART(dd, GETDATE()) >     
 (MONTH(@BirthDate) * 100) + DATEPART(dd, @BirthDate)
 THEN 1             
 ELSE 0             
 END        
 )

2
DECLARE @DOB datetime
set @DOB ='11/25/1985'

select floor(
( cast(convert(varchar(8),getdate(),112) as int)-
cast(convert(varchar(8),@DOB,112) as int) ) / 10000
)

źródło: http://beginsql.wordpress.com/2012/04/26/how-to-calculate-age-in-sql-server/


Krótszy sposób zrobienia tego w SQL Server 2012+ jest następujący i pozwala uniknąć konwersji na numer 112, a podłoga nie jest wymagana:code: SELECT [Age] = (0+ FORMAT(@ToDate,'yyyyMMdd') - FORMAT(@DOB,'yyyyMMdd') ) /10000
ukgav

2

Dużo myślałem i szukałem w tej sprawie i mam 3 rozwiązania

  • poprawnie obliczyć wiek
  • są krótkie (głównie)
  • są (w większości) bardzo zrozumiałe.

Oto wartości testowe:

DECLARE @NOW DATETIME = '2013-07-04 23:59:59' 
DECLARE @DOB DATETIME = '1986-07-05' 

Rozwiązanie 1: Znalazłem to podejście w jednej bibliotece js. To jest moje ulubione.

DATEDIFF(YY, @DOB, @NOW) - 
  CASE WHEN DATEADD(YY, DATEDIFF(YY, @DOB, @NOW), @DOB) > @NOW THEN 1 ELSE 0 END

W rzeczywistości dodaje różnicę lat do DOB, a jeśli jest większa niż bieżąca data, odejmuje jeden rok. Proste, prawda? Tyle tylko, że tutaj powielają się różnice lat.

Ale jeśli nie potrzebujesz go używać w tekście, możesz napisać to w ten sposób:

DECLARE @AGE INT = DATEDIFF(YY, @DOB, @NOW)
IF DATEADD(YY, @AGE, @DOB) > @NOW
SET @AGE = @AGE - 1

Rozwiązanie 2: Ten, który pierwotnie skopiowałem z @ bacon-bits. Najłatwiejszy do zrozumienia, ale trochę długi.

DATEDIFF(YY, @DOB, @NOW) - 
  CASE WHEN MONTH(@DOB) > MONTH(@NOW) 
    OR MONTH(@DOB) = MONTH(@NOW) AND DAY(@DOB) > DAY(@NOW) 
  THEN 1 ELSE 0 END

To w zasadzie obliczanie wieku, tak jak my, ludzie.


Rozwiązanie 3: Mój przyjaciel przerobił to na to:

DATEDIFF(YY, @DOB, @NOW) - 
  CEILING(0.5 * SIGN((MONTH(@DOB) - MONTH(@NOW)) * 50 + DAY(@DOB) - DAY(@NOW)))

Ten jest najkrótszy, ale najtrudniejszy do zrozumienia. 50to tylko waga, więc różnica dni jest ważna tylko wtedy, gdy miesiące są takie same. SIGNFunkcja służy do przekształcania dowolnej otrzymanej wartości na -1, 0 lub 1. CEILING(0.5 *jest tym samym Math.max(0, value), co w SQL, ale nie ma czegoś takiego.


1
CASE WHEN datepart(MM, getdate()) < datepart(MM, BIRTHDATE) THEN ((datepart(YYYY, getdate()) - datepart(YYYY, BIRTH_DATE)) -1 )
     ELSE 
        CASE WHEN datepart(MM, getdate()) = datepart(MM, BIRTHDATE)
            THEN 
                CASE WHEN datepart(DD, getdate()) < datepart(DD, BIRTHDATE) THEN ((datepart(YYYY, getdate()) - datepart(YYYY, BIRTHDATE)) -1 )
                    ELSE (datepart(YYYY, getdate()) - datepart(YYYY, BIRTHDATE))
                END
        ELSE (datepart(YYYY, getdate()) - datepart(YYYY, BIRTHDATE)) END            
    END

1
select floor((datediff(day,0,@today) - datediff(day,0,@birthdate)) / 365.2425) as age

Jest tutaj wiele odpowiedzi 365,25. Pamiętaj, jak zdefiniowano lata przestępne:

  • Co cztery lata
    • z wyjątkiem co 100 lat
      • z wyjątkiem co 400 lat

Doskonała odpowiedź. Dla tych, którzy są ciekawi, jest wyjaśnienie, dlaczego 365,2425 jest poprawną wartością do użycia: grc.nasa.gov/WWW/k-12/Numbers/Math/Mathematical_Thinking/ ...
vvvv4d

0

Co powiesz na to:

SET @Age = CAST(DATEDIFF(Year, @DOB, @Stamp) as int)
IF (CAST(DATEDIFF(DAY, DATEADD(Year, @Age, @DOB), @Stamp) as int) < 0) 
    SET @Age = @Age - 1

0

Spróbuj tego

DECLARE @date datetime, @tmpdate datetime, @years int, @months int, @days int
SELECT @date = '08/16/84'

SELECT @tmpdate = @date

SELECT @years = DATEDIFF(yy, @tmpdate, GETDATE()) - CASE WHEN (MONTH(@date) > MONTH(GETDATE())) OR (MONTH(@date) = MONTH(GETDATE()) AND DAY(@date) > DAY(GETDATE())) THEN 1 ELSE 0 END
SELECT @tmpdate = DATEADD(yy, @years, @tmpdate)
SELECT @months = DATEDIFF(m, @tmpdate, GETDATE()) - CASE WHEN DAY(@date) > DAY(GETDATE()) THEN 1 ELSE 0 END
SELECT @tmpdate = DATEADD(m, @months, @tmpdate)
SELECT @days = DATEDIFF(d, @tmpdate, GETDATE())

SELECT Convert(Varchar(Max),@years)+' Years '+ Convert(Varchar(max),@months) + ' Months '+Convert(Varchar(Max), @days)+'days'

0

Wypróbuj to rozwiązanie:

declare @BirthDate datetime
declare @ToDate datetime

set @BirthDate = '1/3/1990'
set @ToDate = '1/2/2008'
select @BirthDate [Date of Birth], @ToDate [ToDate],(case when (DatePart(mm,@ToDate) <  Datepart(mm,@BirthDate)) 
        OR (DatePart(m,@ToDate) = Datepart(m,@BirthDate) AND DatePart(dd,@ToDate) < Datepart(dd,@BirthDate))
        then (Datepart(yy, @ToDate) - Datepart(yy, @BirthDate) - 1)
        else (Datepart(yy, @ToDate) - Datepart(yy, @BirthDate))end) Age

0

To poprawnie rozwiąże problemy z datą urodzenia i zaokrąglaniem:

DECLARE @dob  datetime
SET @dob='1992-01-09 00:00:00'

SELECT DATEDIFF(YEAR, '0:0', getdate()-@dob)

2
Nie obsługuje poprawnie lat przestępnych SELECT DATEDIFF (YEAR, '0: 0', convert (datetime, '2014-02-28') -'2012-02-29 ') daje 2, ale powinno być tylko 1
Peter Kerr

0

Rozwiązanie Eda Harpera jest najprostszym, jakie znalazłem, i nigdy nie zwraca złej odpowiedzi, gdy miesiąc i dzień dwóch dat są oddalone o 1 lub mniej dni. Dokonałem niewielkiej modyfikacji, aby poradzić sobie z negatywnymi wiekami.

DECLARE @D1 AS DATETIME, @D2 AS DATETIME
SET @D2 = '2012-03-01 10:00:02'
SET @D1 = '2013-03-01 10:00:01'
SELECT
   DATEDIFF(YEAR, @D1,@D2)
   +
   CASE
      WHEN @D1<@D2 AND DATEADD(YEAR, DATEDIFF(YEAR,@D1, @D2), @D1) > @D2
      THEN - 1
      WHEN @D1>@D2 AND DATEADD(YEAR, DATEDIFF(YEAR,@D1, @D2), @D1) < @D2
      THEN 1
      ELSE 0
   END AS AGE

0

Odpowiedź oznaczona jako poprawna jest bliższa dokładności, ale zawodzi w następującym scenariuszu - gdzie rok urodzenia to rok przestępny i dzień po miesiącu lutym

declare @ReportStartDate datetime = CONVERT(datetime, '1/1/2014'),
@DateofBirth datetime = CONVERT(datetime, '2/29/1948')

FLOOR(DATEDIFF(HOUR,@DateofBirth,@ReportStartDate )/8766)


LUB

FLOOR(DATEDIFF(HOUR,@DateofBirth,@ReportStartDate )/8765.82) -- Divisor is more accurate than 8766

- Poniższe rozwiązanie daje mi dokładniejsze wyniki.

FLOOR(DATEDIFF(YEAR,@DateofBirth,@ReportStartDate) - (CASE WHEN DATEADD(YY,DATEDIFF(YEAR,@DateofBirth,@ReportStartDate),@DateofBirth) > @ReportStartDate THEN 1 ELSE 0 END ))

Działał w prawie wszystkich scenariuszach, biorąc pod uwagę rok przestępny, datę 29 lutego itp.

Proszę mnie poprawić, jeśli ta formuła ma jakąkolwiek lukę.


Ostateczna formuła jest prawie dokładna, jak w tej odpowiedzi stackoverflow.com/a/1572235/168747 . Ale ten tutaj jest mniej czytelny i zawiera niepotrzebne floor.
Marek

0
Declare @dob datetime
Declare @today datetime

Set @dob = '05/20/2000'
set @today = getdate()

select  CASE
            WHEN dateadd(year, datediff (year, @dob, @today), @dob) > @today 
            THEN datediff (year, @dob, @today) - 1
            ELSE datediff (year, @dob, @today)
        END as Age

0

Oto jak obliczam wiek na podstawie daty urodzenia i aktualnej daty.

select case 
            when cast(getdate() as date) = cast(dateadd(year, (datediff(year, '1996-09-09', getdate())), '1996-09-09') as date)
                then dateDiff(yyyy,'1996-09-09',dateadd(year, 0, getdate()))
            else dateDiff(yyyy,'1996-09-09',dateadd(year, -1, getdate()))
        end as MemberAge
go

0
CREATE function dbo.AgeAtDate(
    @DOB    datetime,
    @CompareDate datetime
)

returns INT
as
begin

return CASE WHEN @DOB is null
THEN 
    null
ELSE 
DateDiff(yy,@DOB, @CompareDate) 
- CASE WHEN datepart(mm,@CompareDate) > datepart(mm,@DOB) OR (datepart(mm,@CompareDate) = datepart(mm,@DOB) AND datepart(dd,@CompareDate) >= datepart(dd,@DOB))
  THEN 0 
  ELSE 1
  END
END
End

GO

-1: będzie źle za każdym razem, month(compare) > month(dob)ALE day(compare) < day(dob), np select dbo.AgeAtDate('2000-01-14', '2016-02-12').
JonBrave,

Masz rację, dziękuję za tę sprawę, zaktualizowałem funkcję
Vova

0
DECLARE @FromDate DATETIME = '1992-01-2623:59:59.000', 
        @ToDate   DATETIME = '2016-08-10 00:00:00.000',
        @Years INT, @Months INT, @Days INT, @tmpFromDate DATETIME
SET @Years = DATEDIFF(YEAR, @FromDate, @ToDate)
 - (CASE WHEN DATEADD(YEAR, DATEDIFF(YEAR, @FromDate, @ToDate),
          @FromDate) > @ToDate THEN 1 ELSE 0 END) 


SET @tmpFromDate = DATEADD(YEAR, @Years , @FromDate)
SET @Months =  DATEDIFF(MONTH, @tmpFromDate, @ToDate)
 - (CASE WHEN DATEADD(MONTH,DATEDIFF(MONTH, @tmpFromDate, @ToDate),
          @tmpFromDate) > @ToDate THEN 1 ELSE 0 END) 

SET @tmpFromDate = DATEADD(MONTH, @Months , @tmpFromDate)
SET @Days =  DATEDIFF(DAY, @tmpFromDate, @ToDate)
 - (CASE WHEN DATEADD(DAY, DATEDIFF(DAY, @tmpFromDate, @ToDate),
          @tmpFromDate) > @ToDate THEN 1 ELSE 0 END) 

SELECT @FromDate FromDate, @ToDate ToDate, 
       @Years Years,  @Months Months, @Days Days

0

A co z rozwiązaniem zawierającym tylko funkcje daty, a nie matematykę, bez obaw o rok przestępny

CREATE FUNCTION dbo.getAge(@dt datetime) 
RETURNS int
AS
BEGIN
    RETURN 
        DATEDIFF(yy, @dt, getdate())
        - CASE 
            WHEN 
                MONTH(@dt) > MONTH(GETDATE()) OR 
                (MONTH(@dt) = MONTH(GETDATE()) AND DAY(@dt) > DAY(GETDATE())) 
            THEN 1 
            ELSE 0 
        END
END

0

Po wypróbowaniu WIELU metod działa to w 100% przypadków przy użyciu nowoczesnej funkcji MS SQL FORMAT zamiast konwertowania do stylu 112. Albo zadziała, ale jest to najmniejszy kod.

Czy ktoś może znaleźć niedziałającą kombinację dat? Chyba nie ma :)

--Set parameters, or choose from table.column instead:

DECLARE @DOB    DATE = '2000/02/29' -- If @DOB is a leap day...
       ,@ToDate DATE = '2018/03/01' --...there birthday in this calculation will be 

--0+ part tells SQL to calc the char(8) as numbers:
SELECT [Age] = (0+ FORMAT(@ToDate,'yyyyMMdd') - FORMAT(@DOB,'yyyyMMdd') ) /10000

-2

Użyliśmy czegoś takiego jak tutaj, ale biorąc pod uwagę średni wiek:

ROUND(avg(CONVERT(int,DATEDIFF(hour,DOB,GETDATE())/8766.0)),0) AS AverageAge

Zwróć uwagę, ROUND jest raczej na zewnątrz niż wewnątrz. Dzięki temu AVG będzie dokładniejsze, a ROUND tylko raz. Robiąc to też szybciej.


-2
select DATEDIFF(yy,@DATE,GETDATE()) -
case when DATEPART(mm,GETDATE())*100+DATEPART(dd,GETDATE())>=
DATEPART(mm,@DATE)*100+DATEPART(dd,@DATE) THEN 0
ELSE 1 END 

-2
SELECT CAST(DATEDIFF(dy, @DOB, GETDATE()+1)/365.25 AS int)

Widzę głosy w dół, ale nie widzę przykładów, które dowodzą, że to jest zepsute.
Alex
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.