วิธีคำนวณอายุ (เป็นปี) ตามวันเดือนปีเกิดและ getDate ()


171

ฉันมีรายชื่อตารางคนพร้อมกับวันเกิดของพวกเขา (ปัจจุบันเป็น nvarchar (25))

ฉันจะแปลงให้เป็นวันที่แล้วคำนวณอายุของพวกเขาเป็นปี ๆ ได้อย่างไร

ข้อมูลของฉันมีลักษณะดังนี้

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

ฉันอยากจะดู:

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

16
เหตุใดคุณจึงเก็บค่าวันที่เป็นสตริงโดยใช้ nvarchar (25) แทนที่จะใช้วันที่ดั้งเดิมหรือประเภทวันที่และเวลา
Jesper

คำถามถูกติดแท็กในปี 2005 ไม่ใช่ปี 2008 ดังนั้นจึงไม่มีประเภท 'วันที่' ดั้งเดิม แต่แน่นอนว่าเป็นวันที่และเวลาและมันอาจเป็นที่ถกเถียงกันอยู่ว่า SmallDateTime เนื่องจากคุณไม่ต้องการความแม่นยำ
Andrew

สวัสดีเหตุผลสำหรับการรักษาวันที่เป็น varchar เป็นเพราะผมนำเข้าจากสคีเซิร์ฟเวอร์ SQL ที่ไม่ใช่มีบางประเด็นที่นำเข้าพวกเขาเป็น datetime (และรูปแบบวันที่อื่น ๆ ) และ varchar แปลง ok
จิมมี่

7
@ James.Elsey คุณมีปัญหาในการนำเข้าและดังนั้นวันที่ทั้งหมดจะใช้ได้หรือไม่ ไม่สามารถมั่นใจได้เว้นแต่ว่าคุณจะใช้วันที่และเวลาหรือวันเล็ก ๆ ด้วย varchar คุณอาจได้รับการนำเข้าของคุณไปใช้งานได้ นอกจากนี้ฉันไม่เคยเก็บอายุไว้มันเปลี่ยนแปลงในแต่ละวันใช้มุมมอง
KM

@KM ใช่มีปัญหาการนำเข้าข้อมูลนั้นเป็นวันที่ทางออกที่ทำงานได้เพียงอย่างเดียวในขณะนั้นคือการนำเข้าเป็น nvarchars ตัวเลือกนี้จะเป็นส่วนหนึ่งของงานทุกคืนดังนั้นการจัดเก็บอายุไม่ควรมีปัญหา
Jimmy

คำตอบ:


256

มีปัญหาเกี่ยวกับเผ่นปี / วันและวิธีการต่อไปนี้ดูการปรับปรุงด้านล่าง:

ลองนี้:

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

เอาท์พุท:

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

(1 row(s) affected)

การปรับปรุงที่นี่เป็นวิธีที่แม่นยำยิ่งขึ้น:

วิธีที่ดีที่สุดสำหรับปีใน 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

คุณสามารถเปลี่ยนข้างต้น10000เป็น10000.0รับทศนิยมได้ แต่จะไม่แม่นยำเท่าวิธีการด้านล่าง

วิธีที่ดีที่สุดสำหรับปีในช่วงสิบปี

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
นี่ไม่ใช่วิธีแก้ปัญหาที่แน่นอน หากฉันใช้ @dob ของตัวเองเป็น '1986-07-05 00:00:00' และฉันจะดำเนินการนี้ (ใช้ตัวแปรอื่นแทนGETDATE()) ใน '2013-07-04 23:59:59' มันบอกว่าฉัน ตอนที่ 27 ในขณะนั้นฉันยังไม่ โค้ดตัวอย่าง: 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
สิ่งนี้ไม่ถูกต้องเนื่องจากใช้เวลา 8766 ชั่วโมงต่อปีซึ่งใช้งานได้ 365.25 วัน เนื่องจากไม่มีปีที่มี 365.25 วันสิ่งนี้จะไม่ถูกต้องใกล้กับวันเกิดของบุคคลบ่อยกว่าที่ถูกต้อง วิธีนี้ยังคงแม่นยำยิ่งขึ้น
เบคอนบิตส์

1
ความคิดเห็น @Bacon Bits ที่สอง - มักจะผิดเมื่อวันที่ปัจจุบันใกล้วันเกิดของบุคคล
แฟลช

2
ฉันคิดว่าบล็อกข้อความแรกทำให้คำตอบนี้สับสน หากวิธีการอัปเดตของคุณไม่มีปัญหาเกี่ยวกับปีอธิกสุรทินฉันขอแนะนำ (ถ้าคุณต้องการเก็บไว้จริงๆ) เพื่อเลื่อนลงด้านล่างของคำตอบของคุณ
ajbeaven

1
หากคุณต้องการการคำนวณที่แน่นอนจากนั้น DATEDIFF จะไม่ทำเช่นนั้น @ShailendraMishra เพราะจะใกล้เคียงกับวัน ฯลฯ ตัวอย่างเช่นselect datediff(year, '2000-01-05', '2018-01-04')ส่งคืน 18 ไม่ใช่ 17 เท่าที่ควร ฉันใช้ตัวอย่างข้างต้นภายใต้หัวข้อ "วิธีที่ดีที่สุดสำหรับปีใน INT" และทำงานได้อย่างสมบูรณ์แบบ ขอบคุณ!
openwonk

132

ต้องโยนอันนี้ออกไป ถ้าคุณแปลงวันที่โดยใช้สไตล์ 112 (yyyymmdd) เป็นตัวเลขคุณสามารถใช้การคำนวณแบบนี้ ...

(yyyyMMdd - yyyyMMdd) / 10000 = ความแตกต่างในปีเต็ม

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

เอาท์พุต

20091015    19800420    290595  29

14
นี่เกือบจะวิเศษในการแก้ไขปัญหาปีอธิกสุรทินทั้งหมด อาจเป็นที่น่าสังเกตว่า 112 เป็นหมายเลขพิเศษสำหรับฟังก์ชัน CONVERT ที่จัดรูปแบบวันที่เป็น yyyymmdd อาจไม่ชัดเจนสำหรับทุกคนเมื่อคุณดู
Derek Tomes

5
คุณคืออัจฉริยะ!
เม.ย.

ทีมของฉันพบปัญหาเมื่อวันที่เราใช้เพื่อค้นหาอายุเป็นวันเดียวกับวันที่เราเปรียบเทียบ เราสังเกตเห็นว่าเมื่อพวกเขาเป็นวันเดียวกัน (และถ้าอายุกำลังจะแปลก) อายุจะถูกปิดโดยหนึ่ง มันทำงานได้อย่างสมบูรณ์แบบ!
The Sheek Geek

1
นี่คือคำตอบที่ถูกต้อง - โปรดทำเครื่องหมายเช่นนี้
Snympi

4
รหัสที่สั้นที่สุด / ง่ายที่สุดสำหรับวิธีการคำนวณแบบเดียวกันนี้ใน SQL Server 2012+ คือ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

ฉันใช้คำค้นหานี้ในรหัสการผลิตของเรามาเกือบ 10 ปีแล้ว:

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

6
มันไม่ได้เลวร้าย แต่ไม่ใช่ 100%, 2007/10/16 จะรายงานอายุ 2 เมื่อ 2009/10/15
Andrew

4
Doh เราหายไปอย่างเห็นได้ชัดมันเป็นหลังจากเที่ยงวัน getdate คืนค่า int ดังนั้นจะมีการปัดเศษขึ้น ฉันคัดลอกวางคำตอบของคุณและวิ่งมันดังนั้นใช้ getdate โดยอัตโนมัติไม่ใช่ตัวอักษร
Andrew

2
สง่างามและเรียบง่าย ขอบคุณ!
William MB

9
หากเรากำลังพูดถึงอายุของมนุษย์คุณควรคำนวณวิธีที่มนุษย์คำนวณอายุ มันไม่มีส่วนเกี่ยวข้องกับความเร็วของโลกที่เคลื่อนไหวและทุกสิ่งที่เกี่ยวข้องกับปฏิทิน ทุกครั้งที่เดือนและวันที่ผ่านไปเหมือนวันเดือนปีเกิดคุณเพิ่มอายุโดย 1 ซึ่งหมายความว่าต่อไปนี้ถูกต้องที่สุดเพราะมันสะท้อนสิ่งที่มนุษย์หมายถึงเมื่อพวกเขาพูดว่า "อายุ":DATEDIFF(yy, @BirthDate, GETDATE()) - CASE WHEN (MONTH(@BirthDate) >= MONTH(GETDATE())) AND DAY(@BirthDate) > DAY(GETDATE()) THEN 1 ELSE 0 END
เบคอน Bits

5
ขออภัยไวยากรณ์นั้นผิด CASE WHEN (MONTH(@date) > MONTH(GETDATE())) OR (MONTH(@date) = MONTH(GETDATE()) AND DAY(@date) > DAY(GETDATE())) THEN 1 ELSE 0 END
เบคอน Bits

31

การแก้ปัญหาข้างต้นจำนวนมากผิด DateDiff (yy, @ Dob, @PassedDate) จะไม่พิจารณาเดือนและวันของทั้งสองวัน นอกจากนี้ยังมีส่วนของโผและการเปรียบเทียบก็ต่อเมื่อได้รับคำสั่งอย่างถูกต้องเท่านั้น

รหัสต่อไปนี้ทำงานและง่ายมาก:

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

ทำไมคุณคูณด้วย 100 สิ่งนี้ใช้ได้กับฉันขณะที่ฉันพยายามทำซ้ำในฐานข้อมูลที่มีอยู่ในไลบรารีรหัสของเรา - แต่ฉันไม่สามารถอธิบายฟังก์ชันของคุณได้ นี้อาจจะเป็นคำถามโง่ :)
เจน

6
ขอบคุณ! รหัสที่ฉันคาดหวังไว้ตรงนี้ นี่เป็นเพียงรหัสที่ถูกต้องเท่านั้นในหัวข้อนี้โดยไม่ต้องแปลงสตริง (น่าเกลียด)! @Jen ใช้เวลาเดือนและวันของ DoB (เช่น 25 กันยายน) และเปลี่ยนเป็นค่าจำนวนเต็ม0925(หรือ925) มันทำเช่นเดียวกันกับวันที่ปัจจุบัน (เช่น 16 ธันวาคมกลายเป็น1216) แล้วตรวจสอบว่าค่าจำนวนเต็ม DoB ได้ผ่านไปแล้ว ในการสร้างจำนวนเต็มนี้เดือนควรถูกคูณด้วย 100
bartlaarhoven

ขอบคุณ @bartlaarhoven :)
เจน

ฉันจะพูดถึงว่าในขณะนี้จะหลีกเลี่ยงการแปลงสตริง แต่มันหล่อมากแทน การทดสอบของฉันแสดงให้เห็นว่ามันไม่ได้เร็วกว่าคำตอบของ dotjoe อย่างมากและรหัสนั้นละเอียดกว่ามาก
StriplingWarrior

คำตอบที่ยอมรับนั้นมีคำตอบ INT ที่ง่ายกว่ามาก:(CONVERT(int,CONVERT(char(8),@Now,112))-CONVERT(char(8),@Dob,112))/10000
KM

19

คุณต้องพิจารณาวิธีที่คำสั่งลงวันที่รอบ

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>

ซึ่งผมดัดแปลงมาจากที่นี่

โปรดทราบว่าจะพิจารณาวันที่ 28 กุมภาพันธ์เป็นวันเกิดของการกระโดดข้ามปีแบบไม่กระโดดเช่นผู้ที่เกิดวันที่ 29 กุมภาพันธ์ 2563 จะได้รับการพิจารณาอายุ 1 ปีในวันที่ 28 กุมภาพันธ์ 2564 แทน 1 มีนาคม 2564


@Andrew - ถูกแก้ไข - ฉันพลาดหนึ่งในการเปลี่ยนตัว
Ed Harper

1
รุ่นที่ง่ายขึ้นSELECT DATEDIFF(year, DOB, getdate()) + CASE WHEN (DATEADD(year,DATEDIFF(year, DOB, getdate()) , DOB) > getdate()) THEN - 1 ELSE 0 END)
Peter

นี่คือวิธีการที่ถูกต้อง; ฉันไม่เข้าใจว่าทำไมการแฮ็กจึงถูกอัปโหลดมากเกินไป
Salman

8

แก้ไข: คำตอบนี้ไม่ถูกต้อง ฉันปล่อยไว้ที่นี่เพื่อเป็นการเตือนผู้ที่อยากใช้dayofyearพร้อมกับแก้ไขเพิ่มเติมในตอนท้าย


หากเช่นเดียวกับคุณคุณไม่ต้องการหารด้วยวันเศษส่วนหรือมีข้อผิดพลาดในการปัดเศษ / ปีอธิกสุรทินฉันปรบมือให้ @Bacon Bits ความคิดเห็นในโพสต์ด้านบนhttps://stackoverflow.com/a/1572257/489865ที่เขาพูดว่า:

หากเรากำลังพูดถึงอายุของมนุษย์คุณควรคำนวณวิธีที่มนุษย์คำนวณอายุ มันไม่มีส่วนเกี่ยวข้องกับความเร็วของโลกที่เคลื่อนไหวและทุกสิ่งที่เกี่ยวข้องกับปฏิทิน ทุกครั้งที่เดือนและวันที่ผ่านไปเหมือนวันเดือนปีเกิดคุณเพิ่มอายุโดย 1 ซึ่งหมายความว่าต่อไปนี้ถูกต้องที่สุดเพราะมันสะท้อนสิ่งที่มนุษย์หมายถึงเมื่อพวกเขาพูดว่า "อายุ"

จากนั้นเขาก็เสนอ:

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

มีข้อเสนอแนะหลายประการเกี่ยวกับการเปรียบเทียบเดือนและวัน (และบางข้อผิดพลาดทำให้ไม่สามารถORทำได้อย่างถูกต้องที่นี่!) แต่ไม่มีใครเสนอdayofyearซึ่งดูเหมือนง่ายและสั้นกว่ามาก ฉันเสนอ:

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

[หมายเหตุ: ไม่มีที่ไหนใน SQL BOL / MSDN คือสิ่งที่DATEPART(dayofyear, ...)ส่งคืนเอกสารจริง! ฉันเข้าใจว่ามันเป็นตัวเลขในช่วง 1--366; ที่สำคัญที่สุดมันไม่ได้เปลี่ยนตามภาษาตามDATEPART(weekday, ...)& SET DATEFIRST.]


แก้ไข: ทำไมdayofyearผิดพลาด : ในฐานะที่เป็นผู้ใช้ @AeroX มีความเห็นว่าถ้าเกิด / วันที่เริ่มต้นคือหลังจากเดือนกุมภาพันธ์ปีอธิกสุรทินบุหรี่, อายุจะเพิ่มขึ้นวันหนึ่งต้นเมื่อวันที่ / สิ้นสุดปัจจุบันเป็นปีอธิกสุรทินเช่น'2015-05-26', '2016-05-25'ให้ อายุ 1 เมื่อมันควรจะเป็น 0 เมื่อเปรียบเทียบdayofyearในปีที่แตกต่างกันนั้นเป็นอันตรายอย่างชัดเจน ดังนั้นการใช้MONTH()และDAY()เป็นสิ่งจำเป็นหลังจากทั้งหมด


สิ่งนี้ควรถูกโหวตหรือทำเครื่องหมายว่าเป็นคำตอบ มันสั้นหรูหราและถูกต้องตามหลักเหตุผล
z00l

1
สำหรับทุกคนที่เกิดหลังจากเดือนกุมภาพันธ์อายุของพวกเขาจะเพิ่มขึ้นหนึ่งวันก่อนทุกปีอธิกสุรทินโดยใช้DayOfYearวิธีการ
AeroX

4
@ AeroX ขอบคุณสำหรับการพบข้อบกพร่องนี้ ฉันตัดสินใจที่จะออกจากการแก้ปัญหาของฉันเป็นคำเตือนให้กับทุกคนที่อาจหรือเคยใช้dayofyearแต่แก้ไขอย่างชัดเจนเพื่อแสดงว่าทำไมมันผิดไป ฉันหวังว่ามันจะเหมาะสม
JonBrave

5

เนื่องจากไม่มีคำตอบง่ายๆที่ให้อายุที่ถูกต้องเสมอนี่คือสิ่งที่ฉันคิดขึ้นมา

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 

สิ่งนี้ได้รับผลต่างปีระหว่างวันเดือนปีเกิดและวันที่ปัจจุบัน จากนั้นจะลบปีหากวันเกิดยังไม่ผ่าน

แม่นยำตลอดเวลา - ไม่ว่าจะปีกระโดดหรือใกล้วันเกิด

ดีที่สุดของทั้งหมด - ไม่มีฟังก์ชั่น


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

1
คุณต้องการ getdate เป็นอาร์กิวเมนต์ที่สองไม่ใช่อันดับแรกมิฉะนั้นคุณจะได้ผลลัพธ์เป็นลบและรอบวันที่ดังนั้นให้เลือกลงวันที่ (yy, '20081231', getdate ()) จะรายงานอายุ 1 แต่จะมีอายุเพียง 10 เดือน .
Andrew

1
การคำนวณนี้ให้การคำนวณที่ไม่ถูกต้องสำหรับผู้ที่ยังไม่มีวันเกิดในปีนี้

3

เกี่ยวกับ:

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

นั่นจะไม่หลีกเลี่ยงปัญหาการปัดเศษการตัดและการตั้งค่าทั้งหมดหรือไม่


การคำนวณของคุณไม่ถูกต้อง ตัวอย่างเช่น: ล้มเหลวหากคุณใช้'1986-07-05 00:00:00'DOB และ'2013-07-04 23:59:59'เป็นเวลาปัจจุบัน
drinovc

@ub_coding คุณเสนอและตอบหรือถามคำถามอื่นหรือไม่
แอรอน C

นี่คือ DECLARE @DOB datetime SET @ DOB = '19760229' เลือก Date Date (yy, conversion (datetime, '19770228') - @ DOB) -1900 = 1 ปัญหาหลักคือ fev 29 gap สำหรับการแก้ปัญหาส่วนใหญ่ .
Leonardo Marques de Souza

3

ฉันเชื่อว่านี่คล้ายกับโพสต์อื่นที่นี่ .... แต่วิธีนี้ใช้ได้กับตัวอย่างปีอธิกสุรทิน 02/29/1976 ถึง 03/01/2011 และยังใช้ได้กับกรณีในปีแรก .. เช่น 07/04 / 2011 ถึง 07/03/2012 ซึ่งโพสต์ล่าสุดเกี่ยวกับโซลูชัน leap year ไม่สามารถใช้งานได้ในกรณีที่ใช้ในปีแรก

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

พบที่นี่


3

เพียงตรวจสอบว่าคำตอบด้านล่างเป็นไปได้

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
)

แหล่งที่มา: http://beginsql.wordpress.com/2012/04/26/how-to-calculate-age-in-sql-server/


วิธีที่สั้นกว่าในการทำเช่นนี้ใน SQL Server 2012+ มีดังต่อไปนี้และหลีกเลี่ยงการแปลงเป็น 112 และไม่จำเป็นต้องใช้พื้น:code: SELECT [Age] = (0+ FORMAT(@ToDate,'yyyyMMdd') - FORMAT(@DOB,'yyyyMMdd') ) /10000
ukgav

2

ฉันได้คิดมากและค้นหาเกี่ยวกับสิ่งนี้และฉันมีวิธีแก้ปัญหา 3 ข้อ

  • คำนวณอายุอย่างถูกต้อง
  • สั้น (ส่วนใหญ่)
  • เป็นส่วนใหญ่เข้าใจได้มาก

นี่คือค่าทดสอบ:

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

โซลูชันที่ 1:ฉันพบวิธีนี้ในไลบรารี js เดียว มันเป็นที่ชื่นชอบ

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

จริง ๆ แล้วมันเป็นการเพิ่มความแตกต่างในปี DOB และถ้ามันใหญ่กว่าวันที่ปัจจุบันให้ลบหนึ่งปี ง่ายใช่มั้ย สิ่งเดียวคือความแตกต่างในปีที่ทำซ้ำที่นี่

แต่ถ้าคุณไม่จำเป็นต้องใช้มันแบบอินไลน์คุณสามารถเขียนแบบนี้:

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

โซลูชันที่ 2: อันนี้ฉันคัดลอกมาจาก @ bacon-bits มันง่ายที่สุดที่จะเข้าใจ แต่ยาวไปหน่อย

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

มันเป็นการคำนวณอายุตามที่เราเป็นมนุษย์


โซลูชันที่ 3:เพื่อนของฉัน refactored ลงในนี้:

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

อันนี้สั้นที่สุด แต่ก็ยากที่จะเข้าใจ 50เป็นเพียงน้ำหนักดังนั้นความแตกต่างของวันนั้นสำคัญเมื่อเดือนเหมือนกันเท่านั้น SIGNฟังก์ชั่นสำหรับการแปลงค่าอะไรก็ตามที่ได้รับถึง -1, 0 หรือ 1 CEILING(0.5 *เหมือนกันMath.max(0, value)แต่ไม่มีสิ่งนั้นใน 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

มีคำตอบมากมาย 365.25 ข้อที่นี่ โปรดจำไว้ว่าการกำหนดปีอธิกสุรทิน:

  • ทุกๆสี่ปี
    • ยกเว้นทุก ๆ 100 ปี
      • ยกเว้นทุก ๆ 400 ปี

คำตอบที่ยอดเยี่ยม สำหรับผู้ที่อยากรู้อยากเห็นที่นี่เป็นคำอธิบายว่าทำไม 365.2425 เป็นค่าที่ถูกต้องในการใช้: grc.nasa.gov/WWW/k-12/Numbers/Math/Mathematical_Thinking/ ......
vvvv4d

0

เกี่ยวกับสิ่งนี้:

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

ลองสิ่งนี้

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

ลองวิธีนี้:

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

สิ่งนี้จะจัดการกับปัญหาเกี่ยวกับวันเกิดและการปัดเศษอย่างถูกต้อง:

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

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

2
ไม่จัดการกับการก้าวกระโดดอย่างถูกต้องเลือก DATEDIFF (ปี, '0: 0', แปลง (วันที่และเวลา, '2014-02-28') -'2012-02-29 'ให้ 2, แต่ควรจะเป็น 1
Peter Kerr

0

วิธีแก้ปัญหาของเอ็ดฮาร์เปอร์นั้นง่ายที่สุดที่ฉันได้พบซึ่งไม่เคยได้คำตอบที่ผิดกลับมาเมื่อเดือนและวันของสองวันนั้นห่างกัน 1 วันหรือน้อยกว่านั้น ฉันทำการปรับเปลี่ยนเล็กน้อยเพื่อจัดการกับอายุเชิงลบ

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

คำตอบที่ทำเครื่องหมายว่าถูกต้องนั้นใกล้เคียงกับความถูกต้อง แต่มันล้มเหลวในสถานการณ์ต่อไปนี้ - โดยที่ปีเกิดเป็นปีที่ Leap ปีและวันหลังจากเดือนกุมภาพันธ์

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

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


หรือ

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

- การแก้ปัญหาต่อไปนี้คือการให้ผลลัพธ์ที่แม่นยำยิ่งขึ้นแก่ฉัน

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

มันทำงานได้ในเกือบทุกสถานการณ์โดยพิจารณาจากปีอธิกสุรทินวันที่ 29 กุมภาพันธ์เป็นต้นไป

โปรดแก้ไขฉันหากสูตรนี้มีช่องโหว่ใด ๆ


สูตรสุดท้ายนั้นเกือบจะเหมือนกันทุกประการในคำตอบนี้stackoverflow.com/a/1572235/168747 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

นี่คือวิธีที่ฉันคำนวณอายุที่ได้รับวันเดือนปีเกิดและวันที่ปัจจุบัน

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: จะผิดเวลาใด ๆmonth(compare) > month(dob)แต่เช่นday(compare) < day(dob) select dbo.AgeAtDate('2000-01-14', '2016-02-12')
JonBrave

คุณพูดถูกขอบคุณสำหรับกรณีนี้ได้มีการปรับปรุงฟังก์ชั่น
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

วิธีแก้ปัญหาที่มีเฉพาะฟังก์ชันวันที่ไม่ใช่คณิตศาสตร์ไม่ต้องกังวลเกี่ยวกับปีอธิกสุรทิน

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

หลังจากลองวิธี MANY วิธีนี้ใช้งานได้ 100% ของเวลาโดยใช้ฟังก์ชัน MS SQL FORMAT ที่ทันสมัยแทนการแปลงเป็นรูปแบบ 112 ซึ่งอาจใช้งานได้ แต่นี่เป็นรหัสน้อยที่สุด

ใครสามารถหาชุดค่าผสมวันที่ที่ไม่ทำงาน? ฉันไม่คิดว่าจะมีอย่างใดอย่างหนึ่ง :)

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

เราใช้บางสิ่งบางอย่างเช่นที่นี่ แต่เมื่อถึงวัยโดยเฉลี่ย:

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

ขอให้สังเกตว่า ROUND อยู่ด้านนอกมากกว่าภายใน สิ่งนี้จะช่วยให้ AVG มีความแม่นยำมากขึ้นและเราจะปัดเศษเพียงครั้งเดียว ทำให้เร็วขึ้นด้วย



โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.