Расчет возраста SQL - точный способ - PullRequest
0 голосов
/ 04 июня 2019

Я пробовал три способа, все работают, но получают разные результаты:

  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)
    

Первый я получаю 63, второй и третий я получаю 62, какой из них является точным?

Ответы [ 4 ]

1 голос
/ 04 июня 2019

Просто используйте древний алгоритм эпохи мэйнфреймов:

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
0 голосов
/ 04 июня 2019

Попробуйте использовать эту скалярную функцию. Требуется три параметра.

Дата начала и окончания, а также дополнительная опция для добавления одного дня.

Функция возвращает результат в виде «ГГ ММ ДД». Есть несколько примеров

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 

А исходный код указан во фрагменте ниже.

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;
0 голосов
/ 04 июня 2019

Если вы хотите чей-то возраст, возьмите разницу в «год», а затем вычтите ее на основе заказа MMDD. Итак:

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

Это должно быть точно для високосных лет и високосных дней и увеличивать возраст только в чей-то день рождения (или, если день рождения 29 февраля, то 1 марта).

Вы также можете сформулировать выражение case, используя представление MMDD и выполнив:

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

Методы, использующие DATEDIFF(), просто не работают (легко), потому что DATEDIFF() не учитывает разницу между двумя периодами, а скорее количество временных границ между ними.

Использование разницы в днях и деление на 365,25 является приблизительным и будет примерно в день рождения.

Использование правил календаря (например, выше) должно давать правильные результаты.

0 голосов
/ 04 июня 2019

Возраст человека в день его рождения в году равен (year - BirthYear). Это то, что вычисляется с помощью параметра yy.Если у них не было дня рождения, вы должны вычесть один год, это то, что делает мой ИИФ.

Как вы обнаружили, другие методы имеют недостатки.Например, я узнал, что дни / 365,25 иногда бывают неправильными в отношении дня рождения человека, и это дополнительная хитрость, если он родился 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
        )
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...