Cách tính tuổi (tính theo năm) dựa trên Ngày sinh và getDate ()


171

Tôi có một bảng liệt kê những người cùng với ngày sinh của họ (hiện là người gốc (25))

Làm thế nào tôi có thể chuyển đổi nó thành một ngày, và sau đó tính tuổi của họ theo năm?

Dữ liệu của tôi trông như sau

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

Tôi muốn nhìn thấy:

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

16
Tại sao bạn lưu trữ giá trị ngày dưới dạng chuỗi bằng nvarchar (25) thay vì sử dụng loại ngày hoặc loại thời gian gốc của cơ sở dữ liệu?
Jesper

Câu hỏi được gắn thẻ năm 2005 không phải năm 2008 vì vậy loại 'Ngày' gốc không có sẵn, nhưng chắc chắn là một thời gian, và nó có thể được tranh luận là SmallDateTime vì bạn không cần độ chính xác.
Andrew

Hi, lý do để giữ ngày như varchar là bởi vì tôi đang nhập khẩu này từ một schema máy chủ không SQL, đã có một số vấn đề nhập khẩu chúng như datetime (và các định dạng ngày khác) và varchar chuyển ok
Jimmy

7
@ James.Elsey, vì vậy bạn đã gặp sự cố khi nhập và kết quả là tất cả các ngày có hợp lệ không? không bao giờ có thể chắc chắn trừ khi bạn sử dụng datetime hoặc smalldatetime, với varchar, bạn có thể làm cho quá trình nhập của bạn hoạt động, nhưng có vấn đề khác. Ngoài ra, tôi sẽ không bao giờ lưu trữ tuổi, nó thay đổi mỗi ngày, sử dụng Chế độ xem
KM.

@KM Có một vấn đề khi nhập dữ liệu đó vào một ngày, giải pháp khả thi duy nhất tại thời điểm đó là nhập chúng dưới dạng nvarchar. Lựa chọn này sẽ là một phần của công việc hàng đêm vì vậy việc lưu trữ độ tuổi không phải là vấn đề
Jimmy

Câu trả lời:


256

Có vấn đề với năm nhuận / ngày và phương pháp sau, xem bản cập nhật dưới đây:

thử cái này:

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

ĐẦU RA:

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

(1 row(s) affected)

CẬP NHẬT ở đây là một số phương pháp chính xác hơn:

PHƯƠNG PHÁP TỐT NHẤT CHO NĂM TRONG 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

bạn có thể thay đổi ở trên 10000thành 10000.0và lấy số thập phân, nhưng nó sẽ không chính xác như phương pháp bên dưới.

PHƯƠNG PHÁP TỐT NHẤT CHO NĂM TRONG KHAI THÁC

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
Đây cũng không phải là một giải pháp chính xác. Nếu tôi lấy @dob của riêng mình thành '1986-07-05 00:00:00' và tôi sẽ thực hiện điều này (sử dụng một biến khác thay vì GETDATE()) vào '2013-07-04 23:59:59' thì nó nói rằng tôi 27 tuổi, trong lúc đó, tôi chưa. Mã ví dụ: 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 16/12/13

20
Điều này là không chính xác vì nó giả định 8766 giờ mỗi năm, hoạt động tới 365,25 ngày. Vì không có năm nào với 365,25 ngày, điều này sẽ không chính xác gần ngày sinh của người đó thường xuyên hơn là chính xác. Phương pháp này vẫn sẽ chính xác hơn.
Bacon Bits

1
Nhận xét thứ hai @Bacon Bits - điều này thường sẽ sai khi ngày hiện tại gần đến ngày sinh của một người.
flash

2
Tôi nghĩ rằng khối văn bản đầu tiên làm cho câu trả lời này khó hiểu. Nếu phương pháp cập nhật của bạn không có vấn đề với năm nhuận, tôi đề nghị (nếu bạn thực sự muốn giữ nó) để chuyển nó xuống cuối câu trả lời của bạn.
ajbeaven

1
Nếu bạn muốn tính toán CHÍNH XÁC , thì DATEDIFF sẽ không thực hiện @ShailendraMishra, vì nó xấp xỉ ngày, v.v. Ví dụ, select datediff(year, '2000-01-05', '2018-01-04')trả về 18, không phải 17 như bình thường. Tôi đã sử dụng ví dụ trên dưới tiêu đề "PHƯƠNG PHÁP TỐT NHẤT CHO NĂM TRONG INT" và nó hoạt động hoàn hảo. Cảm ơn!
openwonk

132

Phải ném cái này ra khỏi đó. Nếu bạn chuyển đổi ngày bằng cách sử dụng kiểu 112 (yyyymmdd) thành số bạn có thể sử dụng phép tính như thế này ...

(yyyyMMdd - yyyyMMdd) / 10000 = chênh lệch trong cả năm

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

đầu ra

20091015    19800420    290595  29

14
Điều này gần như kỳ diệu trong cách nó giải quyết tất cả các vấn đề năm nhuận. Có thể đáng chú ý rằng 112 là một số đặc biệt cho hàm CONVERT định dạng ngày là yyyymmdd. Nó có thể không rõ ràng cho mọi người khi bạn nhìn vào nó.
Derek Tomes

5
Bạn là một thiên tài!
ercan

Nhóm của tôi đã gặp phải một vấn đề khi ngày chúng tôi đang sử dụng để tìm tuổi là cùng ngày với ngày chúng tôi đang so sánh. Chúng tôi đã nhận thấy rằng khi họ ở cùng một ngày (và nếu tuổi sẽ là số lẻ) thì tuổi sẽ bị giảm đi. Điều này làm việc hoàn hảo!
The Sheek Geek

1
Đây là câu trả lời đúng - vui lòng đánh dấu nó như vậy.
Snympi

4
Mã đơn giản / ngắn nhất cho phương pháp tính toán chính xác tương tự này trong SQL Server 2012+ làcode: 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

Tôi đã sử dụng truy vấn này trong mã sản xuất của chúng tôi trong gần 10 năm:

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

6
Nó không tệ, nhưng không phải là 100%, 2007/10/16 sẽ báo cáo tuổi 2 vào 2009/10/15
Andrew

4
Doh, chúng tôi đang bỏ lỡ điều hiển nhiên, đó là sau giữa ngày, getdate trả về một int nên tất nhiên sẽ làm tròn số. Tôi sao chép dán câu trả lời của bạn và chạy nó, vì vậy tự động sử dụng getdate, không phải theo nghĩa đen.
Andrew

2
Thanh lịch và đơn giản. Cảm ơn!
William MB

9
Nếu chúng ta đang nói về tuổi của con người, bạn nên tính theo cách con người tính tuổi. Nó không liên quan gì đến việc trái đất di chuyển nhanh như thế nào và mọi thứ phải làm với lịch. Mỗi lần cùng tháng và ngày trôi qua là ngày sinh, bạn tăng tuổi lên 1. Điều này có nghĩa là chính xác nhất bởi vì nó phản ánh ý nghĩa của con người khi họ nói "tuổi":DATEDIFF(yy, @BirthDate, GETDATE()) - CASE WHEN (MONTH(@BirthDate) >= MONTH(GETDATE())) AND DAY(@BirthDate) > DAY(GETDATE()) THEN 1 ELSE 0 END
Bacon Bits

5
Xin lỗi, cú pháp đó là sai. CASE WHEN (MONTH(@date) > MONTH(GETDATE())) OR (MONTH(@date) = MONTH(GETDATE()) AND DAY(@date) > DAY(GETDATE())) THEN 1 ELSE 0 END
Bacon Bits

31

Vì vậy, nhiều giải pháp trên là sai DateDiff (yy, @ Dob, @PassedDate) sẽ không xem xét tháng và ngày của cả hai ngày. Cũng lấy các bộ phận phi tiêu và so sánh chỉ hoạt động nếu chúng được đặt hàng đúng.

CÁC CÔNG TRÌNH MÃ SAU VÀ RẤT ĐƠN GIẢN:

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

Tại sao bạn nhân với 100? Điều này hoạt động với tôi khi tôi đang cố gắng sao chép trong cơ sở dữ liệu những gì tồn tại trong thư viện mã của chúng tôi - nhưng tôi không thể giải thích chức năng của bạn. Đây có thể là một câu hỏi ngu ngốc :)
Jen

6
Cảm ơn! Chính xác là mã tôi đã mong đợi ở đây. Đây là mã chính xác duy nhất trong luồng này mà không có biến đổi chuỗi (xấu)! @Jen Mất tháng và ngày của DoB (như ngày 25 tháng 9) và biến nó thành một giá trị nguyên 0925(hoặc 925). Nó cũng tương tự với ngày hiện tại (như ngày 16 tháng 12 trở đi 1216) và sau đó kiểm tra xem giá trị số nguyên DoB đã qua chưa. Để tạo số nguyên này, tháng cần được nhân với 100.
bartlaarhoven 16/12/13

Cảm ơn @bartlaarhoven :)
Jen

Tôi sẽ chỉ đề cập rằng trong khi điều này không tránh được các phép biến đổi chuỗi, thì nó thực hiện rất nhiều lần thay thế. Thử nghiệm của tôi cho thấy nó không nhanh hơn đáng kể so với câu trả lời của dotjoe và mã này dài dòng hơn.
StriplingWar Warrior

câu trả lời được chấp nhận có câu trả lời INT đơn giản hơn nhiều:(CONVERT(int,CONVERT(char(8),@Now,112))-CONVERT(char(8),@Dob,112))/10000
KM.

19

Bạn cần xem xét cách các vòng lệnh dateiff.

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>

Mà tôi thích nghi từ đây .

Lưu ý rằng nó sẽ coi ngày 28 tháng 2 là ngày sinh nhật của một năm không nhảy vọt, ví dụ một người sinh ngày 29 tháng 2 năm 2020 sẽ được coi là 1 tuổi vào ngày 28 tháng 2 năm 2021 thay vì ngày 1 tháng 3 năm 2021.


@Andrew - Đã sửa - Tôi đã bỏ lỡ một trong những sự thay thế
Ed Harper

1
Phiên bản đơn giản hóaSELECT DATEDIFF(year, DOB, getdate()) + CASE WHEN (DATEADD(year,DATEDIFF(year, DOB, getdate()) , DOB) > getdate()) THEN - 1 ELSE 0 END)
Peter

Đây là cách tiếp cận đúng; Tôi không hiểu tại sao hack lại được nâng cấp rất nhiều.
Salman A

8

EDIT: TRẢ LỜI NÀY LÀ TUYỆT VỜI. Tôi để nó ở đây như một lời cảnh báo cho bất kỳ ai muốn sử dụng dayofyear, với một chỉnh sửa thêm vào cuối.


Nếu, giống như tôi, bạn không muốn chia theo ngày phân số hoặc rủi ro làm tròn / lỗi năm nhuận, tôi hoan nghênh bình luận của @Bacon Bits trong một bài đăng trên https://stackoverflow.com/a/1572257/489865 nơi ông nói:

Nếu chúng ta đang nói về tuổi của con người, bạn nên tính theo cách con người tính tuổi. Nó không liên quan gì đến việc trái đất di chuyển nhanh như thế nào và mọi thứ phải làm với lịch. Mỗi lần cùng tháng và ngày trôi qua là ngày sinh, bạn tăng tuổi lên 1. Điều này có nghĩa là sau đây là chính xác nhất vì nó phản ánh ý nghĩa của con người khi họ nói "tuổi".

Sau đó, ông cung cấp:

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

Có một số gợi ý ở đây liên quan đến việc so sánh tháng & ngày (và một số sai lầm, không cho phép ORchính xác ở đây!). Nhưng không ai đã cung cấp dayofyear, có vẻ như rất đơn giản và ngắn hơn nhiều. Tôi đề nghị:

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

[Lưu ý: Không nơi nào trong SQL BOL / MSDN là những gì DATEPART(dayofyear, ...)trả về thực sự được ghi lại! Tôi hiểu nó là một số trong phạm vi 1--366; quan trọng nhất, nó không thay đổi theo miền địa phương theo DATEPART(weekday, ...)& SET DATEFIRST.]


EDIT: Tại sao dayofyearđi sai : Khi sử dụng @AeroX đã nhận xét, nếu sinh / ngày bắt đầu là sau tháng Hai trong một năm nhuận thuốc, độ tuổi được tăng lên một ngày đầu khi ngày / kết thúc hiện nay là một năm nhuận, ví dụ '2015-05-26', '2016-05-25'đưa ra một tuổi 1 khi vẫn còn bằng 0. So sánh dayofyeartrong những năm khác nhau rõ ràng là nguy hiểm. Vì vậy, sử dụng MONTH()DAY()là cần thiết sau khi tất cả.


Điều này nên được bỏ phiếu lên hoặc thậm chí được đánh dấu là câu trả lời. Nó ngắn gọn, thanh lịch và hợp lý.
z00l

1
Đối với mọi người sinh sau tháng Hai, Tuổi của họ được tăng lên sớm một ngày vào mỗi năm nhuận bằng DayOfYearphương pháp này.
AeroX

4
@AeroX Cảm ơn bạn đã phát hiện ra lỗ hổng này. Tôi quyết định để lại giải pháp của mình như một lời cảnh báo cho bất kỳ ai có thể hoặc đã sử dụng dayofyear, nhưng được chỉnh sửa rõ ràng để cho thấy tại sao nó lại sai. Tôi hy vọng đó là phù hợp.
JonBrave

5

Vì không có một câu trả lời đơn giản nào luôn đưa ra độ tuổi chính xác, đây là những gì tôi nghĩ ra.

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 

Điều này nhận được sự khác biệt năm giữa ngày sinh và ngày hiện tại. Sau đó, nó sẽ trừ đi một năm nếu ngày sinh chưa trôi qua.

Chính xác mọi lúc - bất kể năm nhuận hay gần với ngày sinh.

Tốt nhất của tất cả - không có chức năng.


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

1
Bạn muốn getdate là đối số thứ hai không phải là đối số thứ nhất nếu không bạn nhận được kết quả số âm và các vòng ngày, vì vậy hãy chọn dateiff (yy, '20081231', getdate ()) sẽ báo cáo 1 tuổi, nhưng chúng chỉ được 10 tháng tuổi .
Andrew

1
Tính toán này đưa ra các tính toán không chính xác cho những người chưa có sinh nhật năm nay.

3

Thế còn:

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

Điều đó có tránh được tất cả những vấn đề làm tròn, cắt xén và giải quyết vấn đề không?


Bạn tính toán không chính xác. Ví dụ: thất bại nếu bạn dùng '1986-07-05 00:00:00'DOB và '2013-07-04 23:59:59'cho thời điểm hiện tại.
drinovc

@ub_coding Bạn đang cung cấp và trả lời hoặc hỏi một câu hỏi khác?
Aaron C


3

Tôi tin rằng nó tương tự như những cái khác được đăng ở đây .... nhưng giải pháp này đã hoạt động cho các ví dụ về năm nhuận 02/29/1976 đến 03/01/2011 và cũng hoạt động cho trường hợp trong năm đầu tiên .. như 07/04 / 2011 đến 07/03/2012 mà giải pháp cuối cùng được đăng về giải pháp năm nhuận không hoạt động cho trường hợp sử dụng năm đầu tiên đó.

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

Tìm thấy ở đây .


3

Chỉ cần kiểm tra xem câu trả lời dưới đây là khả thi.

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
)

nguồn: http://beginsql.wordpress.com/2012/04/26/how-to-calculate-age-in-sql-server/


Một cách ngắn hơn để thực hiện việc này trong SQL Server 2012+ như sau và tránh chuyển đổi thành 112 và không bắt buộc:code: SELECT [Age] = (0+ FORMAT(@ToDate,'yyyyMMdd') - FORMAT(@DOB,'yyyyMMdd') ) /10000
ukgav

2

Tôi đã suy nghĩ và tìm kiếm rất nhiều về điều này và tôi có 3 giải pháp

  • tính tuổi chính xác
  • ngắn (chủ yếu)
  • là (hầu hết) rất dễ hiểu.

Dưới đây là các giá trị thử nghiệm:

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

Giải pháp 1: Tôi tìm thấy cách tiếp cận này trong một thư viện js. Nó là sở thích của tôi.

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

Nó thực sự bổ sung sự khác biệt về số năm cho DOB ​​và nếu nó lớn hơn ngày hiện tại thì trừ đi một năm. Đơn giản phải không? Điều duy nhất là sự khác biệt trong năm được nhân đôi ở đây.

Nhưng nếu bạn không cần sử dụng nội tuyến, bạn có thể viết nó như thế này:

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

Giải pháp 2: Cái này ban đầu tôi đã sao chép từ @ bacon-bits. Đó là cách dễ hiểu nhất nhưng hơi dài.

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

Về cơ bản, đó là tính tuổi như con người chúng ta.


Giải pháp 3: Bạn tôi đã tái cấu trúc nó thành cái này:

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

Cái này ngắn nhất nhưng khó hiểu nhất. 50chỉ là một trọng lượng nên sự khác biệt trong ngày chỉ quan trọng khi các tháng là như nhau. SIGNHàm là để chuyển đổi bất kỳ giá trị nào nó đạt được -1, 0 hoặc 1. CEILING(0.5 *giống như Math.max(0, value)nhưng không có điều đó trong SQL.


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

Có rất nhiều câu trả lời 365,25 ở đây. Hãy nhớ cách năm nhuận được định nghĩa:

  • Bốn năm một lần
    • ngoại trừ cứ sau 100 năm
      • ngoại trừ cứ sau 400 năm

Câu trả lời tuyệt vời. Đối với những người tò mò ở đây là một lời giải thích tại sao 365.2425 là giá trị chính xác để sử dụng: grc.nasa.gov/WWW/k-12/Numbers/Math/Mathologists_Thinking / Khăn
vvvv4d

0

Còn cái này thì sao:

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

Thử cái này

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

Hãy thử giải pháp này:

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

Điều này sẽ xử lý chính xác các vấn đề với ngày sinh nhật và làm tròn:

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

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

2
Không xử lý các năm nhuận một cách chính xác CHỌN NGÀY (NĂM, '0: 0', chuyển đổi (datetime, '2014 / 02-28') -'2012-02-29 ') cho 2, nhưng chỉ nên là 1
Peter Kerr

0

Giải pháp của Ed Harper là cách đơn giản nhất mà tôi đã tìm thấy không bao giờ trả lời sai khi tháng và ngày của hai ngày cách nhau 1 hoặc ít hơn. Tôi đã thực hiện một sửa đổi nhỏ để xử lý độ tuổi tiêu cực.

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

Câu trả lời được đánh dấu là chính xác gần với độ chính xác hơn, nhưng nó thất bại trong kịch bản sau - trong đó Năm sinh là năm nhuận và ngày là sau tháng hai

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

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


HOẶC LÀ

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

- Giải pháp sau đây là cho tôi kết quả chính xác hơn.

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

Nó hoạt động trong hầu hết các kịch bản, xem xét năm nhuận, ngày là 29 feb, v.v.

Xin vui lòng sửa cho tôi nếu công thức này có bất kỳ kẽ hở.


Công thức cuối cùng gần như chính xác như trong câu trả lời này stackoverflow.com/a/1572235/168747 . Nhưng một ở đây là ít đọc và chứa không cần thiết 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

Đây là cách tôi tính tuổi đưa ra ngày sinh và ngày hiện tại.

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: Sẽ sai bất cứ lúc nào month(compare) > month(dob)NHƯNG day(compare) < day(dob), vd select dbo.AgeAtDate('2000-01-14', '2016-02-12').
JonBrave 17/2/2016

Bạn đã đúng, cảm ơn bạn vì trường hợp này, đã cập nhật chức năng
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

Thế còn một giải pháp chỉ có chức năng ngày tháng, không phải toán học, không phải lo lắng về năm nhuận

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

Sau khi thử NHIỀU phương thức, công cụ này hoạt động 100% thời gian bằng cách sử dụng hàm FORMAT MS SQL hiện đại thay vì chuyển đổi sang kiểu 112. Hoặc sẽ hoạt động nhưng đây là mã ít nhất.

Bất cứ ai cũng có thể tìm thấy một sự kết hợp ngày không hoạt động? Tôi không nghĩ có một cái :)

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

Chúng tôi đã sử dụng một cái gì đó như ở đây, nhưng sau đó lấy độ tuổi trung bình:

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

Lưu ý, VÒNG ở bên ngoài chứ không phải bên trong. Điều này sẽ cho phép AVG chính xác hơn và chúng tôi chỉ ROUND một lần. Làm cho nó nhanh hơn quá.


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

Tôi thấy bỏ phiếu nhưng tôi không thấy các ví dụ chứng minh điều này bị hỏng.
Alex
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.