¿Dónde está la tabla DUAL en Teradata?

10 diciembre \10\+02:00 2009

Todo aquél que llega a Teradata procedente de entornos Oracle pregunta lo mismo ‘¿Aquí no hay tabla DUAL?’. La respuesta suele ser ‘No. No hace falta.’.

En efecto: en Teradata un ‘SELECT’ no tiene por qué tener un ‘FROM’:

 BTEQ -- Enter your DBC/SQL request or BTEQ command:
SELECT DATE;

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

    Date
--------
09/12/10

 BTEQ -- Enter your DBC/SQL request or BTEQ command:
SELECT 1 DUMMY;

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

DUMMY
-----
    1

Esto al principio le deja a uno un poco ‘a cuadros’, acostumbrado a años y años de ‘SELECT SYSDATE FROM DUAL’ (‘OK. Me adapto y ya está.’).

Entonces uno se acuerda de esto:

SQL*Plus: Release 10.2.0.1.0 - Production on Jue Dic 10 20:21:08 2009

Copyright (c) 1982, 2005, Oracle.  All rights reserved.

Introduzca el nombre de usuario: carlos@XE.localhost
Introduzca la contraseña:

Conectado a:
Oracle Database 10g Express Edition Release 10.2.0.1.0 - Production

CARLOS@XE.localhost> SELECT *
  2    FROM ( SELECT 1 FROM DUAL
  3           UNION ALL
  4           SELECT 2 FROM DUAL
  5           UNION ALL
  6           SELECT 3 FROM DUAL );

         1
----------
         1
         2
         3

… e intenta hacer algo como esto:

 BTEQ -- Enter your DBC/SQL request or BTEQ command:
SELECT *
  FROM ( SELECT 1
         UNION ALL
         SELECT 2
         UNION ALL
         SELECT 3 );

 *** Failure 3707 Syntax error, expected something like a name or a Unicode
 delimited identifier or an 'UDFCALLNAME' keyword between ')' and ';'.
                Statement# 1, Info =108
 *** Total elapsed time was 1 second.

‘¡Ah!, es que en Terdata todas las SUBSELECTs deben llevar alias’:

SELECT *
  FROM ( SELECT 1
         UNION ALL
         SELECT 2
         UNION ALL
         SELECT 3 ) a;

SELECT *
$
 *** Failure 3706 Syntax error: All expressions in a derived table must
have an explicit name.
                Statement# 1, Info =0
 *** Total elapsed time was 1 second.

‘¡Ah!, es que en Terdata todas las columnas deben llevar nombre’.

Pero entonces ocurre lo siguiente:

 BTEQ -- Enter your DBC/SQL request or BTEQ command:
SELECT *
  FROM ( SELECT 1 DUMMY
         UNION ALL
         SELECT 2 DUMMY
         UNION ALL
         SELECT 3 DUMMY
       ) a
;

 *** Failure 3888 A SELECT for a UNION,INTERSECT or MINUS must
reference a table.
                Statement# 1, Info =0
 *** Total elapsed time was 1 second.

¿Pero, no habíamos quedado en que en Teradata no hacía falta un ‘FROM’? Ya. Pero parece que en las UNION, INTERSECT o MINUS sí.

¿Entonces, qué hacemos sin nuestra tabla DUAL?

Aquí hay gente que decide recurrir a cualquier tabla de utilidad:

 BTEQ -- Enter your DBC/SQL request or BTEQ command:
SELECT *
  FROM ( SELECT 1 DUMMY
           FROM SYS_CALENDAR.CALENDAR
          WHERE DAY_OF_CALENDAR = 1
         UNION ALL
         SELECT 2 DUMMY
           FROM SYS_CALENDAR.CALENDAR
          WHERE DAY_OF_CALENDAR = 1
         UNION ALL
         SELECT 3 DUMMY
           FROM SYS_CALENDAR.CALENDAR
          WHERE DAY_OF_CALENDAR = 1
       ) a;

 *** Query completed. 3 rows found. One column returned.
 *** Total elapsed time was 1 second.

DUMMY
-----
    1
    2
    3

Pero, en el fondo, no es más que un rodeo (‘workaround‘, lo llaman los anglosajones) para soslayar el problema de la falta de tabla DUAL.

Otra solución es crearnos nuestra propia tabla DUAL, con una única columna ‘DUMMY’ y una única fila ‘X’ -a imagen y semejanza de la añorada DUAL de Oracle-. Otro rodeo.

Pero lo que queremos no es rodear el problema. Queremos resolverlo. ¿Cómo crear tablas sobre la marcha con ‘UNION’ en Teradata?

Teniendo en cuenta lo que hemos dicho arriba sobre la obligatorieded de alias y nombres para tablas y columnas, la solución es más fácil de lo que parece:

 BTEQ -- Enter your DBC/SQL request or BTEQ command:
SELECT *
  FROM ( SELECT 1 COL FROM ( SELECT 'X' DUMMY ) a
         UNION ALL
         SELECT 2 FROM ( SELECT 'X' DUMMY ) b
         UNION ALL
         SELECT 3 FROM ( SELECT 'X' DUMMY ) c
       ) d
;

 *** Query completed. 3 rows found. One column returned.
 *** Total elapsed time was 1 second.

 COL
----
   1
   2
   3

Una cosa más para los nostálgicos: no utilicéis ‘DUAL’ como alias de tabla, ya que en TERADATA DUAL es una palabra reservada:

SELECT *
  FROM ( SELECT 1 COL FROM ( SELECT 'X' DUMMY ) DUAL
         UNION ALL
         SELECT 2 FROM ( SELECT 'X' DUMMY ) DUAL
         UNION ALL
         SELECT 3 FROM ( SELECT 'X' DUMMY ) DUAL
       ) d
;

         UNION ALL
$
 *** Failure 3707 Syntax error, expected something like a name or a Unicode delimited identifier
 or an 'UDFCALLNAME' keyword between ')' and the 'DUAL' keyword.
                Statement# 1, Info =63
 *** Total elapsed time was 1 second.

Cualquier otra valdrá:

 BTEQ -- Enter your DBC/SQL request or BTEQ command:
SELECT *
  FROM ( SELECT 1 COL FROM ( SELECT 'X' DUMMY ) MY_DUAL
         UNION ALL
         SELECT 2 FROM ( SELECT 'X' DUMMY ) MY_DUAL
         UNION ALL
         SELECT 3 FROM ( SELECT 'X' DUMMY ) MY_DUAL
       ) d
;

 *** Query completed. 3 rows found. One column returned.
 *** Total elapsed time was 1 second.

 COL
----
   1
   2
   3

Saludos.

Carlos.