简体   繁体   English

SQL年龄计算-准确的方法

[英]SQL Age Calculation - Accurate way

I have tried three ways, all work but get different results: 我尝试了三种方法,所有方法都可以,但是得到不同的结果:

  1.  SELECT @age = DATEDIFF(YY, x.BirthDate, x.LastVisitDate) 
  2.  SELECT @age = (CONVERT(int,CONVERT(char(8),x.LastVisitDate,112)) - CONVERT(char(8),x.BirthDate,112)) / 10000 
  3.  SELECT @age = FLOOR(DATEDIFF(DAY, x.BirthDate , x.LastVisitDate) / 365.25) 

The first I get 63, the second and third I get 62, which one is accurate? 第一个得到63,第二个和第三个得到62,哪个是正确的?

Just use the ancient algorithm from the mainframe era: 只需使用大型机时代的古老算法即可:

SELECT @age = (
           (YEAR(x.LastVisitDate) * 10000 + MONTH(x.LastVisitDate) * 100 + DAY(x.LastVisitDate))
          -
           (YEAR(x.BirthDate)* 10000 + MONTH(x.BirthDate) * 100 + DAY(x.BirthDate))
          ) / 10000

A person's age at their birthday in a year is (year - BirthYear) This is what datediff with the yy parameter works out. 一个人一年中的生日年龄是(year-BirthYear)这就是带有yy参数的datediff得出的结果。 If they have not had their birthday, you have to subtract one year, that's what my IIF does. 如果他们还没有生日,那么您必须减去一年,这就是我的IIF所做的。

As you've found, other methods are flawed. 如您所见,其他方法也有缺陷。 For example I experienced that the days/365.25 will sometimes go wrong around a person's birthday, and is extra trick if they were born on feb 29th 例如,我经历过,一个人的生日那天/365.25有时会出错,如果他们出生于2月29日,这是一个额外的技巧

SELECT @age = DATEDIFF(YY, x.BirthDate, x.LastVisitDate) - 
    IIF(MONTH(x.LastVisitDate) < MONTH(x.BirthDate) 
                OR MONTH(x.LastVisitDate) = MONTH(x.BirthDate) AND DAY(x.LastVisitDate) < DAY(x.BirthDate) 
                                  , 1
                                  , 0
        )

Try using this scalar function. 尝试使用此标量函数。 It takes three parameters. 它包含三个参数。

The start and the end date and an extra option to add one day. 开始日期和结束日期以及增加一天的额外选项。

The function returns the result in the form "YY MM DD". 该函数以“ YY MM DD”的形式返回结果。 There are a few examples 有几个例子

SELECT dbo.CalcDate(NULL, GETDATE(), 0); --> NULL
SELECT dbo.CalcDate(GETDATE(), GETDATE(), 0); --> 0 0 0
SELECT dbo.CalcDate(GETDATE(), GETDATE(), 1); ---> 0 0 1
SELECT dbo.CalcDate('20150101', '20161003', 0); ---> 1 9 2 
SELECT dbo.CalcDate('20031101', '20161003', 0); --->12 11 2
SELECT dbo.CalcDate('20040731', '20040601', 0); ---> 0 1 30 
SELECT dbo.CalcDate('20040731', '20040601', 1); ---> 0 2 0 

And the source code is listed in the snippet below. 源代码在下面的代码段中列出。

CREATE FUNCTION [dbo].[CalcDate]
( 
                @dwstart datetime, @dwend datetime,@extraDay bit
)
RETURNS nvarchar(20)
BEGIN
    DECLARE @yy int;
    DECLARE @mm int;
    DECLARE @dd int;
    DECLARE @increment int;
SET @increment = 0;
    DECLARE @monthDay TABLE
    ( 
                            monthno int, monthdayno int
    );
    DECLARE @dStart AS datetime;
    DECLARE @dEnd AS datetime;
INSERT INTO @monthDay
    VALUES (1, 31);
INSERT INTO @monthDay
    VALUES (2, -1);
INSERT INTO @monthDay
    VALUES (3, 31);
INSERT INTO @monthDay
    VALUES (4, 30);
INSERT INTO @monthDay
    VALUES (5, 31);
INSERT INTO @monthDay
    VALUES (6, 30);
INSERT INTO @monthDay
    VALUES (7, 31);
INSERT INTO @monthDay
    VALUES (8, 31);
INSERT INTO @monthDay
    VALUES (9, 30);
INSERT INTO @monthDay
    VALUES (10, 31);
INSERT INTO @monthDay
    VALUES (11, 30);
INSERT INTO @monthDay
    VALUES (12, 31);
--The order of the arguments is not important
IF @dwStart > @dWEnd
BEGIN
SET @dStart = @dWEnd;
SET @dEnd = @dWStart;
    END;
ELSE
BEGIN
SET @dStart = @dWStart;
SET @dEnd = @dWEnd;
    END;
--

DECLARE @d1 AS INT;
SET @d1 = DAY(@dStart);
    DECLARE @d2 AS int;
SET @d2 = DAY(@dEnd);
    IF @d1 > @d2
    BEGIN
SET @increment = (SELECT
        monthdayno
    FROM @monthDay
    WHERE monthno = MONTH(@dStart));
    END;

IF @increment = -1
BEGIN
--Is it a leap year
SET @increment = (SELECT
        CASE
            WHEN ISDATE(CAST(YEAR(@dStart) AS CHAR(4)) + '0229') = 1 THEN 29
            ELSE 28
        END);
    END;

IF @increment != 0
BEGIN
SET @DD = DAY(@dEnd) + @increment - DAY(@dStart) + (CASE
    WHEN @extraDay = 1 THEN 1
    ELSE 0
END);
SET @increment = 1;
    END;
ELSE
BEGIN
SET @dd = DAY(@dEnd) - DAY(@dStart) + (CASE
    WHEN @extraDay = 1 THEN 1
    ELSE 0
END);
    END;
IF (MONTH(@dStart) + @increment) > MONTH(@dEnd)
BEGIN
SET @mm = MONTH(@dEnd) + 12 - (MONTH(@dStart) + @increment);
SET @increment = 1;
    END;
ELSE
BEGIN
SET @mm = MONTH(@dEnd) - (MONTH(@dStart) + @increment);
SET @increment = 0;
    END;
SET @yy = YEAR(@dEnd) - (YEAR(@dStart) + @increment);
    IF @dd >= 31
    BEGIN
SET @mm = @mm + 1;
SET @dd = @dd - 31;
    END;

IF @mm >= 12
BEGIN
SET @yy = @yy + 1;
SET @mm = @mm - 12;
    END;

RETURN (CONVERT(NVARCHAR(2), @yy) + ' ' + CONVERT(NVARCHAR(2), @mm) + ' ' + CONVERT(NVARCHAR(2), @dd));


END;

If you want someone's age, take the difference of "year" and then subtract one based on the ordering of MMDD. 如果要某人的年龄,则取“年”之差,然后根据MMDD的顺序减去一个。 So: 所以:

select (year(x.LastVisitDate) - year(x.BirthDate) -
        (case when month(x.LastVisitDate) < month(x.BirthDate)
              then 1
              when month(x.LastVisitDate) = month(x.BirthDate) and
                   day(x.LastVisitDate) < day(x.BirthDate)
              then 1
              else 0
         end)
       ) as age

This should be accurate for leap years and leap days and only increment the age on someone's birthday (or if the birthday is Feb 29th, then on Mar 1st). 这对于for年和leap日应该是准确的,并且仅在某人的生日(或如果生日是2月29日,然后3月1日)上增加年龄。

You can also phrase the case expression using MMDD representation and doing: 您还可以使用MMDD表示法对case表达式进行措辞,然后执行以下操作:

        (case when ( month(x.LastVisitDate) * 100 + day(x.LastVisitDate) <
                      month(x.BirthDate) * 100 + day(x.BirthDate
                   )
              then 1
              then 1
              else 0
         end)

Methods using DATEDIFF() simply do not work (easily) because DATEDIFF() is not counting the difference between two periods, but rather the number of time boundaries between them. 使用DATEDIFF()方法根本不起作用(很容易),因为DATEDIFF()并不计算两个周期之间的差,而是计算它们之间的时间边界数。

Using the difference in days and dividing by 365.25 is an approximation and is going to be off right around the birthday. 使用天数差异除以365.25,这是一个近似值,并且即将在生日那天结束。

Using calendar rules (such as above) should produce the correct results. 使用日历规则(如上述)应产生正确的结果。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM