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’, 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.