¿Cuántos años tienes (en Teradata)?

Esta es vieja: se trata de calcular la edad basándose en dos fechas dadas (p.ej.: entre “hoy” y la fecha de nacimiento).

En principio la cosa parece fácil: hacer substracciones de las fechas basadas en los días o, mejor, en los meses. Pero la cosa se vuelve un poco más oscura cuando vemos que no todos los meses tienen el mismo número de días (28, 30, 31 incluso 29 en años bisiestos)

Oracle elimina estos engorros mediante una muy útil función: “MONTHS_BETWEEN”, que acepta dos fechas, y devuelve el número de meses entre ambas. La lógica de la función y la aritmética de fechas de Oracle se enargan de todos los cálculos necesarios para devolver la solución deseada (aunque hay que haber actualizado al menos a 9.2.0.6 para evitar molestos ‘bugs’):

SQL*Plus: Release 9.2.0.7.0 - Production on Mar Oct 7 17:49:42 2008

Copyright (c) 1982, 2002, Oracle Corporation.  All rights reserved.


Connected to:
Oracle9i Release 9.2.0.7.0 - 64bit Production
JServer Release 9.2.0.7.0 - Production

SQL>  SELECT MONTHS_BETWEEN(SYSDATE, TO_DATE('19720401','YYYYMMDD')) 
 2 >  / 12 FROM DUAL;

MONTHS_BETWEEN(SYSDATE,TO_DATE('19720401','YYYYMMDD'))/12
---------------------------------------------------------
                                               36,5181297

Con una división por 12 y algún ‘TRUNC’, pan comido.

Es capaz incluso de calcular sin problemas cuando la “fecha de nacimiento” está muy lejos en el pasado:

SQL> SELECT TRUNC(MONTHS_BETWEEN(SYSDATE, TO_DATE('00010101','YYYYMMDD')) 
 2 > / 12) FROM DUAL;

TRUNC(MONTHS_BETWEEN(SYSDATE,TO_DATE('00010101','YYYYMMDD'))/12)
----------------------------------------------------------------
                                                            2007

SQL>

En Teradata las cosas no son tan fáciles, ya que no existe función “MONTHS_BETWEEN” ni ninguna otra análoga, por lo que hay que ‘buscarse la vida’, como dicen los modernos.

Hay soluciones indicadas que se basan en hacer un ‘CAST’ a MONTH(4) (que en realidad son INTERVAL MONTH()) y convertirlo a INTEGER antes de dividirlo por 12 (años):

SELECT CURRENT_DATE, CAST((CURRENT_DATE - 
                           CAST('1972-04-01' AS DATE) MONTH(4)) 
                           AS INTEGER) / 12;

 *** Query completed. One row found. 2 columns returned.
 *** Total elapsed time was 1 second.

    Date  ((Date - '1972-04-01') MONTH/12)
--------  --------------------------------
08/10/07                                36

BTEQ -- Enter your DBC/SQL request or BTEQ command:

La cosa en principio funciona bien, pero se estropea si intentamos calcular la edad de Cristo (más o menos):

SELECT CURRENT_DATE, CAST((CURRENT_DATE - 
                           CAST('0001-01-01' AS DATE) MONTH(4)) 
                           AS INTEGER) / 12;
 
 *** Failure 7453 Interval field overflow.
                Statement# 1, Info =0
 *** Total elapsed time was 1 second.


 BTEQ -- Enter your DBC/SQL request or BTEQ command:

Así que el hábil ‘CAST’ nos deja con el culo al aire para edades ‘provectas’, ya que desborda el máximo admitido por el tipo MONTH (que es 4: 9999 meses); por lo que hay que probar otros caminos.

Hay quien prefiere enredarse con complejas llamadas a ‘EXTRACT’ sobre las fechas e ir extrayendo YEAR, MONTH, DAY e ir construyendo una aritmética con los trozos (CASE incluidas)…

Hay también quien prefiere recurrir a ‘JOINs’ con SYS_CALENDAR.CALENDAR y construir de nuevo la aritmética…

Por mi parte, prefiero volver la vista a la forma en que Teradata almacena fechas. En efecto, Teradata almacena las fechas de una forma particular: mediante enteros (‘INTEGERs’) siguiendo la fórmula:

(YEAR - 1900) * 10000 + MONTH * 100 + DAY

Así:

SELECT CURRENT_DATE, CAST(CURRENT_DATE AS INTEGER);

 *** Query completed. One row found. 2 columns returned.
 *** Total elapsed time was 1 second.

Current Date  Current Date
------------  ------------
  2008-10-07       1081007

 BTEQ -- Enter your DBC/SQL request or BTEQ command:

Entonces, si hacemos algo así como:

SELECT CURRENT_DATE,
       ( CAST(CURRENT_DATE AS INTEGER) - 
         CAST(CAST('1972-04-01' AS DATE) AS INTEGER) ) 
       / 10000 EDAD;

 *** Query completed. One row found. 2 columns returned.
 *** Total elapsed time was 1 second.

Current Date         EDAD
------------  -----------
  2008-10-07           36

 BTEQ -- Enter your DBC/SQL request or BTEQ command:

Y también funciona para calcular la edad que tendría hoy Nerón (por poner un ejemplo):

SELECT CURRENT_DATE,
       ( CAST(CURRENT_DATE AS INTEGER) - 
         CAST(CAST('0037-12-15' AS DATE) AS INTEGER) ) 
       / 10000 EDAD;

 *** Query completed. One row found. 2 columns returned.
 *** Total elapsed time was 1 second.

Current Date         EDAD
------------  -----------
  2008-10-07         1970

 BTEQ -- Enter your DBC/SQL request or BTEQ command:

Saludos.

Carlos.

4 respuestas a ¿Cuántos años tienes (en Teradata)?

  1. Hernann dice:

    Gracias me has salvado la mañana !!!

  2. Iñigo dice:

    Si queremos tener en cuenta el mes y día del año en que nos encontramos…

    case WHEN ((fecha_nacimiento (date)) mod 10000) <= (current_date mod 10000)
    then EDAD
    else EDAD-1

    ¿no?

    • CarlosAL dice:

      No.

       BTEQ -- Enter your SQL request or BTEQ command:
      SELECT CURRENT_DATE,
             ( CAST(CURRENT_DATE AS INTEGER) -
               CAST(CAST('1972-02-06' AS DATE) AS INTEGER) )
             / 10000 EDAD;
      
      
       *** Query completed. One row found. 2 columns returned.
       *** Total elapsed time was 1 second.
      
      Current Date         EDAD
      ------------  -----------
        2012-02-07           40
      
      
       BTEQ -- Enter your SQL request or BTEQ command:
      SELECT CURRENT_DATE,
             ( CAST(CURRENT_DATE AS INTEGER) -
               CAST(CAST('1972-02-07' AS DATE) AS INTEGER) )
             / 10000 EDAD;
      
      
       *** Query completed. One row found. 2 columns returned.
       *** Total elapsed time was 1 second.
      
      Current Date         EDAD
      ------------  -----------
        2012-02-07           40
      
       BTEQ -- Enter your SQL request or BTEQ command:
      SELECT CURRENT_DATE,
             ( CAST(CURRENT_DATE AS INTEGER) -
               CAST(CAST('1972-02-08' AS DATE) AS INTEGER) )
             / 10000 EDAD;
      
      
       *** Query completed. One row found. 2 columns returned.
       *** Total elapsed time was 1 second.
      
      Current Date         EDAD
      ------------  -----------
        2012-02-07           39
      
  3. Alejandro dice:

    lei todo esto pero aun no hayo como obtener la edad de una persona sin tener que usar months_between… en lugar de ello tengo que utilizar INTERVAL. (tengo tanto fecha de nacimiento y el sysdate)

Deja un comentario

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: