Hola, chicos,
Todo está bien ?

En este post me gustaría mostrar cómo calcular la diferencia en años (edad) entre dos fechas en SQL Server, mostrando algunas soluciones y un análisis de rendimiento entre estas opciones para identificar la forma más eficaz de realizar este cálculo.

Como sabes, para solucionar este problema no basta con realizar un simple DATEDIFF(YEAR, @Dt_Inicial, @Dt_Final), ya que la función DATEDIFF(YEAR…) devolverá la diferencia en años sin considerar si las fechas ya “cumplieron su aniversario”.

En una explicación sencilla, la función DATEDIFF(AÑO…) devuelve el cálculo AÑO(@Dt_Final) – AÑO(@Dt_Inicial).

Demostraré por qué esto no funciona para el cálculo de la edad:

Como puede ver, no basta con utilizar la función DATEDIFF(YEAR..) para calcular la edad. En el primer ejemplo, con solo 1 día de diferencia entre las fechas, la función me devolvió que esa diferencia es de 1 año.

En el segundo ejemplo, la función devuelve 30 años de diferencia, pero la primera fecha es el 09/04 y la segunda es el 28/05, es decir, aún no ha cumplido 1 año y el valor correcto sería 29 años.

Cómo calcular la edad usando T-SQL

Ahora demostraré dos formas sencillas de calcular la edad utilizando Transact-SQL.

Cálculo de edad utilizando Transact-SQL y CASE

Para realizar este cálculo, calculamos la diferencia en años entre las fechas y disminuimos en 1 si el mes y día de la fecha inicial es menor que el mes y día de la fecha final.

CREATE FUNCTION [dbo].[fncCalcula_Idade] (@Dt_Nascimento DATETIME, @Dt_Hoje DATETIME)
RETURNS INT
AS BEGIN
  RETURN DATEDIFF(YEAR, @Dt_Nascimento, @Dt_Hoje) + CASE WHEN (MONTH(@Dt_Nascimento) > MONTH(@Dt_Hoje) OR (MONTH(@Dt_Nascimento) = MONTH(@Dt_Hoje) AND DAY(@Dt_Nascimento) > DAY(@Dt_Hoje))) THEN -1 ELSE 0 END
END

Cálculo de edad usando Transact-SQL y división por 365,25

Para realizar este cálculo calculamos la diferencia en días entre las fechas y dividimos este valor entre 365,25. ¿Pero por qué este 0,25? La respuesta es sencilla... Años bisiestos. Esta "técnica" está escrita por mi amigo y especialista en BI, Lucas Galón.

CREATE FUNCTION [dbo].[fncCalcula_Idade2] (@Dt_Nascimento DATETIME, @Dt_Hoje DATETIME)
RETURNS INT
AS BEGIN
  RETURN DATEDIFF(DAY, @Dt_Nascimento, @Dt_Hoje) / 365.25
END

Cómo calcular la edad usando CLR

Ahora demostraré dos formas sencillas de calcular la edad utilizando CLR, cuyo objetivo es obtener una gran ganancia de rendimiento gracias a las optimizaciones del lenguaje C# y Microsoft .NET Framework.

Cálculo de edad mediante CLR y cálculos manuales

Para realizar este cálculo, calculamos la diferencia en años entre las fechas y disminuimos en 1 si el mes y día de la fecha inicial es menor que el mes y día de la fecha final.

using System.Data.SqlTypes;

public partial class UserDefinedFunctions
{
    [Microsoft.SqlServer.Server.SqlFunction]
    public static SqlInt32 fncCalcula_Idade(SqlDateTime Dt_Nascimento, SqlDateTime Dt_Hoje)
    {

        if (Dt_Nascimento.IsNull || Dt_Hoje.IsNull)
            return SqlInt32.Null;

        var anos = Dt_Hoje.Value.Year - Dt_Nascimento.Value.Year;

        if (Dt_Hoje.Value.Month < Dt_Nascimento.Value.Month || (Dt_Hoje.Value.Month == Dt_Nascimento.Value.Month && Dt_Hoje.Value.Day < Dt_Nascimento.Value.Day))
            anos--;

        return anos;
    }
}

Cálculo de edad utilizando CLR y división por 365,25

Para realizar este cálculo, calculamos la diferencia en días entre las fechas y dividimos este valor entre 365,25, como se mencionó anteriormente, pero ahora usando el CLR.

using System;
using System.Data.SqlTypes;

public partial class UserDefinedFunctions
{
    [Microsoft.SqlServer.Server.SqlFunction]
    public static SqlInt32 fncCalcula_Idade2(SqlDateTime Dt_Nascimento, SqlDateTime Dt_Hoje)
    {
        return (Dt_Nascimento.IsNull || Dt_Hoje.IsNull) ? SqlInt32.Null : Convert.ToInt32(Math.Floor((Dt_Hoje.Value - Dt_Nascimento.Value).TotalDays / 365.25));
    }
}

Resultados de las funciones presentadas.

Pruebas de rendimiento

Ahora que he demostrado algunas soluciones diferentes para calcular la edad en SQL Server, compararé el rendimiento de todas ellas y comprobaré cuál es la más eficaz.

Como ya mencioné en el post. SQL Server: comparación de rendimiento entre la función escalar y la función escalar CLR, funciones CLR generalmente funcionará mejor que las funciones T-SQL, al mismo tiempo que las funciones en línea (sin crear UDF) generalmente funcionan mejor que las funciones CLR. Veamos cómo le va a cada solución.

Para estas pruebas, usaré una base de 131,072 fechas aleatorias para calcular la edad en comparación con la fecha actual, y el script para generar esta base lo pondré a disposición a continuación:

Script para generar masa de prueba.

IF (OBJECT_ID('dbo.fncRand') IS NOT NULL) DROP FUNCTION dbo.fncRand
GO
 
CREATE FUNCTION dbo.fncRand(@Numero BIGINT)
RETURNS BIGINT
AS
BEGIN
    RETURN (ABS(CHECKSUM(PWDENCRYPT(N''))) / 2147483647.0) * @Numero
END
GO


IF (OBJECT_ID('dbo.Datas') IS NOT NULL) DROP TABLE dbo.Datas
CREATE TABLE dbo.Datas (
    Data_Inicio DATETIME
)

DECLARE
    @Contador INT = 1,
    @Total INT = 17


INSERT INTO dbo.Datas ( Data_Inicio )
VALUES ( '1970-01-01' )


WHILE(@Contador <= @Total)
BEGIN
    

    INSERT INTO dbo.Datas ( Data_Inicio )
    SELECT 
        DATEADD(SECOND, dbo.fncRand(1491696000), '1970-01-01')
    FROM 
        dbo.Datas


    SET @Contador += 1


END

Guión para la realización de las pruebas.

-- Funções inline
SELECT DATEDIFF(YEAR, Data_Inicio, GETDATE()) + CASE WHEN (MONTH(Data_Inicio) > MONTH(GETDATE()) OR (MONTH(Data_Inicio) = MONTH(GETDATE()) AND DAY(Data_Inicio) > DAY(GETDATE()))) THEN -1 ELSE 0 END
FROM dbo.Datas

SELECT CAST(DATEDIFF(DAY, Data_Inicio, GETDATE()) / 365.25 AS INT)
FROM dbo.Datas

-- Funções T-SQL UDF
SELECT dbo.fncCalcula_Idade(Data_Inicio, GETDATE())
FROM dbo.Datas

SELECT dbo.fncCalcula_Idade2(Data_Inicio, GETDATE())
FROM dbo.Datas

-- Funções CLR
SELECT CLR.dbo.fncCalcula_Idade(Data_Inicio, GETDATE())
FROM dbo.Datas

SELECT CLR.dbo.fncCalcula_Idade2(Data_Inicio, GETDATE())
FROM dbo.Datas

Resultados:

Como se esperaba, las funciones en línea presentaron el mejor rendimiento, CLR presentó un rendimiento similar y las funciones T-SQL tardaron mucho más que las otras 2 soluciones. Además, la solución con división por 365,25 fue mejor que usar CASE en los 3 escenarios.

¡Eso es todo, amigos!
Espero que hayas aprendido a calcular la edad correctamente y con el mejor rendimiento posible.
Un abrazo y hasta la próxima.