Windows Vista… tenían razón.

9 Febrero 2010

Pues sí, tenían razón: Windows Vista es sin duda el peor sistema operativo que me he echado a la cara, que ya es decir (que he visto unos cuantos ¿eh?).

El equipo que compré el verano pasado (con la licencia de Vista pagada -OEM-) ya me ha dado varios ‘Blue Screen Of Death‘. El angelito, sólo con arrancar (es decir, con aplicaciones básicas en arranque: antivirus, ‘firewall‘, controladores…) se come 1,2 GB, que es casi tres veces lo que ocupa el equipo de al lado con Windows XP en condiciones similares. El sonido se distorsiona horriblemente cuando hay mucha actividad (o si simplemente pinchas en el winamp y lo arrastras por la pantalla de allá para acá). El disco duro está continuamente funcionando como una carraca (’swapping‘, me temo). Y eso que el cacharro lleva un Core2 Duo con 3 GB RAM.

Lo último: el ‘ReadyBoost‘. Un invento que se han sacado de la manga en Microsoft para ‘acelerar el sistema’ usando la memoria flash de un dispositivo USB como caché de disco. Lo malo es que te quedas sin el espacio dedicado a esa ‘caché’ para el almacenamiento normal en el dispositivo.

Pero eso no es lo peor, ni mucho menos. Mete el USB, declara un tamaño para el ‘ReadyBoost’ e intenta copiar un fichero al USB cuyo tamaño no exceda el tamaño libre del USB pero sí el tamaño libre menos lo declarado para el ‘ReadyBoost’. El resultado: el proceso de copia canta que va a tardar horas en copiar unos cientos de megas (¡!) y se queda muerto. Lo intentas cancelar y tarda varios (muchos) minutos en volver a la vida.  Intenta ‘finalizar el proceso’ desde el administrador de tareas y… que si quieres arroz, Catalina. Simplemente es incapaz de decirte ‘¡Eh, colega! El dispositivo, con lo que has declarado para el ReadyBoost, está lleno’ y parase normalmente. ¡Qué va! Se queda ahí, catatónico.

Si, como yo, eres un tipo distraído que no se acuerda de que activó el ‘ReadyBoost’ para el USB (para ver de qué iba eso) y de repente ves que una simple copia se queda ‘frita’ sin razón aparente ni mensaje explicativo alguno, y te lleva un buen rato descubrir qué demonios está pasando. Pues al final te ocurre como a mí: que aborreces el Windows Vista aun más, si cabe.

¡Joder, qué a gusto me he quedado!

Saludos.

Carlos.


Si algo funciona bien…

7 Febrero 2010

no lo toques!

Esto es lo que le diría a la gente de COMODO, ya que, después de utilizar su COMODO BackUp con excelentes resultados, sacaron una nueva versión más ‘guay’ (la 2.2), con muchas más posibilidades, colorcitos y botoncitos… pero sin mantener lo que yo quería: simples ‘backups’ en claro (nada de formatos propietarios) de ficheros y directorios con comprobación básica para ‘backups’ incrementales con su nombre, rutas y configuraciones  y que pudiera correr cuando quisiera y que quedaran en el sitio de ‘backup’ como meros directorios con el contenido accesible. Esto es fundamental a la hora de hacer backups a un mismo sitio (un disco USB compartido desde un equipo que hace de servidor de ficheros) y poder hacerlos desde distintos equipos en el mismo directorio (Fotos, Música, etc…). Así se pueden mantener sincronizados de forma relativamente sencilla (y recuperarlos también). Además, puedes coger el USB, enchufarlo al DVD del salón y ver las películas, fotos o escuchar música como quien lava.

Después de tener que reinstalar el XP, me encontré con que me había quedado sin mi herramienta de ‘backups’ y tras probar el nuevo COMODO (y otras herramientas como Ocster Backup) estuve a punto de ponerme a llorar y hacer los backups con rsync en cygwin

Pero esta tarde busqué la versión 1 y la encontré ‘por ahí‘ (ya que la web oficial de COMODO sólo ofrece la 2.2 y no he visto forma de acceder a versiones antiguas). Desinstalé la ‘guay’ e instalé ésta. Tras instalarla me anunció que había nuevas versiones y que si quería actualizar. Contesté que sí y al instante me arrepentí: creí que iba a actalizarse a la infausta 2.2… pero afortunadamente, no. Actualizó a la última versión de la 1 (la 1.0.4.337).

Así que ahora vuelvo a tener los ‘backups’ a los que estaba acostumbrado y otra vez soy feliz como una perdizzz…

Lo dicho: si funciona bien, no lo toques (¡coño!).

Saludos.

Carlos.


Guardar datos de columnas BLOB en Teradata como ficheros usando Windows Scripting Host y OLEDB/ADO.

26 Enero 2010

Hemos visto aquí y aquí cómo guardar ficheros en forma de BLOBs en la base de datos. Ambos métodos son sencillos y bastante flexibles (sobre todo comparándolos con la utilización del SQL Assistant).

Bien, ya tenemos nuestros ficheros en la base de datos, ¿Y ahora qué? ¿Qué pasa si queremos volver a recuperarlos como ficheros en nuestro sistema operativo? ¿Cómo hacemos si nos apetece volver a oir a Peter Gabriel, pero hemos olvidado el USB con los mp3 en casa y tenemos “The Lamb” en una columna BLOB de nuestra tabla?

BTEQ no será de mucha ayuda aquí: su funcionalidad a la hora de exportar LOBs es muy limitada. Pero no todo está perdido: Windows Scripting Host y OLEDB/ADO al rescate.

Recorriendo el camino inverso al que hicimos aquí, podemos -con unas pocas líneas de VisualBasic Script- tener el contenido de nuestros BLOBs de vuelta al sistema de ficheros (Ojo: Hay que acordarse de lo que dijimos de habilitar el tratamiento de LOBs mediante las ‘extended properties’ del OLEDB provider):

'Option Explicit
Option Explicit
'Constantes
Const OutDir = "C:\Basura"

'CommandTypeEnum
Const adCmdText = 1

'StreamTypeEnum
Const adTypeBinary = 1 'Indicates binary data.
Const adTypeText   = 2 'Default. Indicates text data, which is in the character set specified by Charset.

'Stream SaveOptionsEnum
Const	adSaveCreateNotExist	 =	1	'Default, creates	a new	file
Const	adSaveCreateOverwrite =	2	'Completely	overwrite data	in	an	existing	file

Const CONNECTIONSTRING = "Provider=TDOLEDB;Data Source=MY_TD;User Id=carlos;password=xxxxxxxx;Extended Properties=""EnableLOBSupport=Yes;"";" 'OLEDB EnableLOBSupport

Main

Sub Main
   Dim objConnection
   Dim objRecordset
   Dim objStream

   Wscript.Echo  Time & " Let's roll..."
   Set objConnection = WScript.CreateObject("ADODB.Connection")
   Set objRecordset = WScript.CreateObject("ADODB.RecordSet")
   Set objStream = CreateObject("ADODB.Stream")
   objConnection.ConnectionString = CONNECTIONSTRING
   objStream.Type = adTypeBinary
   Wscript.Echo "Conectando a Teradata..."
   objConnection.Open()
   objRecordset.ActiveConnection = objConnection
   objRecordset.Open "SELECT NOM_FICHERO, BLOB_CONTENT " & _
                       "FROM MY_DB.PRUEBA_BLOB " & _
                        "ORDER BY NOM_FICHERO"
   Do While Not objRecordset.EOF
      Wscript.Echo "Guardando " & objRecordset.Fields(0) & " como " & OutDir & "\" & objRecordset.Fields(0)
      objStream.Open
      objStream.Write objRecordset.Fields(1)
      objStream.SaveToFile OutDir & "\" & objRecordset.Fields(0), adSaveCreateOverWrite
      objStream.Close
      objRecordset.Move 1
   Loop
   'Close
   objRecordset.Close
   objConnection.Close
   'CleanUp
   Set objStream = Nothing
   Set objRecordset = Nothing
   Set objConnection = Nothing
   Wscript.Echo Time & " Bye."
End Sub

Lo ejecutamos:

C:\Procesos\CargarBLOBs>cscript Export_BLOB.vbs
Microsoft (R) Windows Script Host versión 5.6
Copyright (C) Microsoft Corporation 1996-2001. Reservados todos los derechos.

19:05:37 Let's roll...
Conectando a Teradata...
Guardando 01-The Lamb Lies Down On Broadway.mp3 como C:\Basura1-The Lamb Lies Down On Broadway.mp3
Guardando 02-Fly on a Windshield.mp3 como C:\Basura2-Fly on a Windshield.mp3
Guardando 03-Broadway Melody of 1974.mp3 como C:\Basura3-Broadway Melody of 1974.mp3
19:05:44 Bye.

C:\Procesos\CargarBLOBs>dir C:\Basura\
 El volumen de la unidad C no tiene etiqueta.
 El número de serie del volumen es: D059-3939

 Directorio de C:\Basura

26/01/2010  18:39              .
26/01/2010  18:39              ..
26/01/2010  19:05        11.636.864 01-The Lamb Lies Down On Broadway.mp3
26/01/2010  19:05        10.528.896 02-Fly on a Windshield.mp3
26/01/2010  19:05         1.327.232 03-Broadway Melody of 1974.mp3
               3 archivos     23.492.992 bytes
               2 dirs  36.883.800.064 bytes libres

C:\Procesos\CargarBLOBs>

Y voilà: en 7 segundos, ahí tenemos otra vez a Rael saliendo del metro en Times Square:

The Lamb En Winamp

The Lamb En Winamp

Saludos.

Carlos.


Insertar ficheros en Teradata como BLOBs con bteq

25 Enero 2010

En un artículo anterior ya se explicaba cómo Insertar ficheros en Teradata como BLOBs usando Windows Scripting Host y OLEDB/ADO.

La historia venía de la necesidad de cargar ficheros en modo ‘batch‘ utilizando algún lenguaje y herramientas que lo permitieran. Por aquel entonces estaba trabajando intensamente con Teradata V2R6 y sus herramientas (Teradata Tools). Pero desde entonces Teradata ha mejorado tanto la una como las otras y a partir de la versión 12.00.00.02 de bteq (Basic Teradata Query) permite la carga de ficheros en columnas LOB (BLOB/CLOB).

La forma de hacerlo es bien fácil, pero la documentación de Teradata se encarga de disimularlo muy bien, ya que es bastante difícil acceder a los pocos detalles importantes del, por otra parte sencillísimo, proceso. Todo se basa en la utilización de ‘LOBCOLS’ y ‘AS DEFERRED’ con ‘IMPORT’, ‘USING’ y -si se quiere cargar varias filas de una vez- ‘REPEAT’.

Así pues, utilizaremos bteq con un comando ‘IMPORT’ que cargue un fichero en el que irá la información necesaria y con un ‘REPEAT USING’ llamaremos a un ‘INSERT’ que hará la carga.

Para la prueba, cargaremos tres ficheros mp3 que he extraído del CD de mi disco favorito: “The Lamb Lies Down On Broadway“, de Genesis, y los he guardado en un directorio del disco duro.

Primer paso: creamos la tabla que albergará los ficheros en BLOB:

BTEQ -- Enter your DBC/SQL request or BTEQ command:
CREATE SET TABLE MY_DB.PRUEBA_BLOB,
           NO FALLBACK,
           NO JOURNAL
           (
              NOM_FICHERO  VARCHAR(64) NOT NULL CHARACTER SET LATIN,
              BLOB_CONTENT BLOB(15M)       NULL
           )
           UNIQUE PRIMARY INDEX (NOM_FICHERO)
;

 *** Table has been created.
 *** Total elapsed time was 1 second.

Tenemos el directorio con los mp3:

C:\>DIR "Musica\Genesis\The Lamb Lies Down On Broadway I"
 El volumen de la unidad C no tiene etiqueta.
 El número de serie del volumen es: D059-3939

 Directorio de C:\Musica\Genesis\The Lamb Lies Down On Broadway I

21/10/2009  19:39              .
21/10/2009  19:39              ..
21/10/2009  19:31        11.636.864 01-The Lamb Lies Down on Broadway.mp3
21/10/2009  19:31        10.528.896 02-Fly on a Windshield.mp3
21/10/2009  19:31         1.327.232 03-Broadway Melody of 1974.mp3
21/10/2009  19:32         5.269.632 04-Cuckoo Cocoon.mp3
21/10/2009  19:32        19.839.104 05-In the Cage.mp3
21/10/2009  19:32         6.627.456 06-The Grand Parade of Lifeless Packaging.mp3
21/10/2009  19:32        13.715.584 07-Back in NYC.mp3
21/10/2009  19:33         5.339.264 08-Hairless Heart.mp3
21/10/2009  19:33         8.898.688 09-Counting Out Time.mp3
21/10/2009  19:33        12.630.144 10-Carpet Crawlers.mp3
21/10/2009  19:37        13.645.952 11-The Chamber of 32 Doors.mp3
              11 archivos    109.458.816 bytes
               2 dirs  36.818.722.816 bytes libres

así que generamos (con cualquier editor de texto, en mi caso Ultraedit 7.20) el fichero que usaremos con ‘IMPORT’: será un fichero de texto con dos campos: la ruta al fichero mp3 en el sistema de ficheros del equipo y el propio nombre del fichero:

C:\Musica\Genesis\The Lamb Lies Down On Broadway I1-The Lamb Lies Down On Broadway.mp3|01-The Lamb Lies Down On Broadway.mp3
C:\Musica\Genesis\The Lamb Lies Down On Broadway I2-Fly on a Windshield.mp3|02-Fly on a Windshield.mp3
C:\Musica\Genesis\The Lamb Lies Down On Broadway I3-Broadway Melody of 1974.mp3|03-Broadway Melody of 1974.mp3

Usamos el “pipe” (‘|’) como separador de campos, aunque podríamos haber utilizado cualquier otro.
Lo guardamos como:
C:\Procesos\CargarBLOBs\BTEQ\Prueba_BLOB.imp,
con lo que tenemos la “infraestructura” completa.

Ahora sólo resta el “script” para el bteq, que resulta ser algo tan sencillo como:

.IMPORT VARTEXT '|' LOBCOLS 1 FILE="C:\Procesos\CargarBLOBs\BTEQ\Prueba_BLOB.imp"
.REPEAT *
USING (blob_content BLOB AS DEFERRED, nom_fichero VARCHAR(64))
INSERT INTO MY_DB.PRUEBA_BLOB (NOM_FICHERO, BLOB_CONTENT)
VALUES (:nom_fichero, :blob_content);

Como dijimos antes, las palabras mágicas son ‘LOBCOLS’, que le dicen a bteq cuantas columnas del fichero ‘IMPORT’ son campos con el nombre de ficheros que irán a columnas BLOB, y ‘AS DEFERRED’ que, junto con el tipo BLOB, indican que el contenido del campo se cargará mandando el contenido del fichero desde el cliente a la base de datos Teradata y de forma separada al resto del registro (El contenido del fichero se enviará a Teradata en trozos de 64 Kb hasta que todo el fichero haya sido transmitido).

El resto es simplemente utilizar ‘REPEAT *’ para que se lea todo el fichero ‘IMPORT’ línea a línea y el ‘USING’ para “mapear” los campos a variables que se usarán en el ‘INSERT’.

BTEQ -- Enter your DBC/SQL request or BTEQ command:
.IMPORT VARTEXT '|' LOBCOLS 1 FILE="C:\Procesos\CargarBLOBs\BTEQ\Prueba_BLOB.imp"
 BTEQ -- Enter your DBC/SQL request or BTEQ command:
.REPEAT *
 BTEQ -- Enter your DBC/SQL request or BTEQ command:
USING (blob_content BLOB AS DEFERRED, nom_fichero VARCHAR(64))
INSERT INTO MY_DB.PRUEBA_BLOB (NOM_FICHERO, BLOB_CONTENT)
                       VALUES (:nom_fichero, :blob_content);
 *** Starting Row 0 at Mon Jan 25 11:53:27 2010

 *** Insert completed. One row added.
 *** Total elapsed time was 1 second.

 *** Insert completed. One row added.
 *** Total elapsed time was 2 seconds.

 *** Insert completed. One row added.
 *** Total elapsed time was 3 seconds.

 *** Warning: Out of data.
 *** Finished at input row 3 at Mon Jan 25 11:53:31 2010
 *** Total number of statements: 3,  Accepted : 3,  Rejected : 0

 *** Total elapsed time was 4 seconds.

 *** Total requests sent to the DBC = 3
 *** Successful requests per second =  0.750

Podemos contar las filas de la tabla:

BTEQ -- Enter your DBC/SQL request or BTEQ command:
SELECT COUNT(1) FROM MY_DB.PRUEBA_BLOB;

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

   Count(1)
-----------
          3

E incluso hacer un SELECT, en el que veremos una imagen hexadecimal de los bytes contenidos en la columna BLOB:

SELECT * FROM MY_DB.PRUEBA_BLOB
ORDER BY NOM_FICHERO;

 *** Query completed. 3 rows found. 2 columns returned.
 *** Total elapsed time was 1 second.

NOM_FICHERO                           BLOB_CONTENT
------------------------------------- ----------------------------------------------------
01-The Lamb Lies Down On Broadway.mp3 49443303000000000D7D545045310000000800000047656E6573
02-Fly on a Windshield.mp3            49443303000000001319545045310000000800000047656E6573
03-Broadway Melody of 1974.mp3        49443303000000001075545045310000000800000047656E6573

Saludos.

Carlos.


Branagh ES Wallander

16 Enero 2010

Acabo de terminar de ver la primera entrega de la segunda temporada del “Wallander” que ha realizado la BBC, con Kenneth Branagh como Kurt Wallander y lo único que puedo decir es que Branagh ES Wallander. Aunque ya he leído algunas novelas de la serie de Henning Mankell, a partir de ahora cada vez que lea una nueva novela del inspector, Kurt Wallander tendrá la cara de Kenneth Branagh. Bien es verdad que personalmente siento una profunda admiración por Branagh, y lo único que le reprocho es que dejara escapar una mujer como Emma Thompson, pero es que el tío lo clava.

De la serie (aunque más que una serie al uso son ‘películas para televisión’) sólo se puede decir una palabra: fantástica. La ambientación, la fotografía, los guiones y -una vez más- Branagh inconmensurable hacen de ella una joya en el estercolero que es (mayoritariamente) la televisión actual.

Desde luego, el que espere del derrotado, siempre triste y lleno de problemas Wallander una serie de acción llena de ritmo, explosiones, peleas, persecuciones y un protagonista cuasi-superhéroe, va de culo… Los que esperamos otra cosa lo disfrutamos como en su día disfrutábamos de Retorno a Brideshead, por poner un ejemplo.

Ojalá nuestra Televisión-pública-sin-publicidad hiciera cosas que se parecieran mínimamente a estas maravillas británicas, y no sacase tanto pecho por bodrios como ‘Cuéntame’.

Saludos.

Carlos.


Logitech X-540

12 Enero 2010

Los Reyes Magos han venido con un poco de retraso, pero se han portado: me han traído unos Logitech X-540 con los que en este momento estoy escuchando a Madeleine Peyroux -y flipando, claro-.

Talmente parece que me esté susurrando a los dos oídos a la vez (¡!).

Saludos.

Carlos.


Adiós 2009. (Que te den…)

31 Diciembre 2009

Por fin nos libramos de este 2009, que creo que ha sido un año horrible para todos (bueno, menos para el Barça).

Por mi parte, aunque han pasado cosas buenas (como que he conocido a un casi-superhéroe con el que he hablado horas y horas de los comics -especialmente Marvel- de mi niñez, adolescencia, juventud y más…) estaba deseando que el maldito año se acabara de una (puta) vez.

Además de cambiar de año, también cambiamos de década. (Como curiosidad, diré que la última vez que cambié de década fue en San Petersburgo, así que llegué dos horas antes al efecto 2000 que el resto de la gente que conozco).

Ah! Y parece que han pasado por este modesto blog el equivalente a dos Bernabéus.

Feliz 2010 a todos los que lean esto, habituales o no.

Con mis mejores deseos.

Saludos.

Carlos.


¿Cómo es un Teradata?

28 Diciembre 2009

La mayoría de las veces los desarrolladores (desde los analistas a los programadores) ven -vemos- la ‘base de datos’ (o ‘el servidor’, o como quiera que le llamemos) como un ente abstracto al que uno se conecta y puede acceder, consultar, monitorizar e incluso configurar. Pero pocas veces tenemos la posibilidad de ‘mirar a la bestia a los ojos’ (Las salas de servidores son el feudo de los ’sistemistas’ y la gente de ‘hardware’).

Pero recientemente hemos instalado un sistema Teradata 5555H de dos nodos y he podido ver, tocar, oir y hasta oler las entrañas de la bestia. Y la verdad es que impresiona.

Teradata 5550 Vista 1

Teradata 5555H Vista 1

Teradata 5550 Vista 2

Teradata 5555H Vista 2

Teradata5550 Vista 3

Teradata 5555H Vista 3

Las fotos no son demasiado buenas, pero es que están hechas con una BlackBerry que me prestó mi compañero S. y en unas condiciones de luz bastante precarias.

Saludos.

Carlos


(Just Like) Starting Over

20 Diciembre 2009

Como los que lean esto a menudo habrán sospechado, esta entrada no tiene nada que ver con la Canción de Lennon.

Ya había dicho que el equipo familiar con el Windows XP llevaba una larga temporada dando problemas y más problemas. Bastante a menudo, el equipo se colgaba y la luz del disco duro se quedaba fija.

Bien, la historia de este equipo comenzó hace tres años, cuando lo compré -a muy buen precio- en un periodo en el que creí que iba a tener un año sabático… que luego no llegó ni a un mes. Iba a ser un laboratorio Linux, y lo fue. Pero luego pasó a ser el PC familiar cuando instalé el XP. Su configuración original traía un disco duro Seagate de 80 GB. Con el tiempo, compré otro, también Seagate, de 300 GB. También amplié la memoria y cambié la tarjeta gráfica, que tantos problemas me dio. Pero el caso es que el viejo disco de 80 GB quedó como disco principal.

Por otra parte, llevaba mucho tiempo diciéndome a mí mismo “Hay que hacer un ‘backup’”. Teniendo como tenía instalado el Paragon Drive Backup era cosa de nada, pero lo he ido dejando y dejando…  luego pasa lo que pasa: que Murphy está ahí esperando su oportunidad.

El caso es que el jueves pasado dijo que no arrancaba más. La BIOS a veces veía el disco, a veces no. Y cuando lo veia, no arrancaba. He actualizado la versión de la BIOS dos veces, por si la cosa mejoraba. Pero nasti de plasti, que dicen -decimos- los castizos.

Probé todo: la consola de recuperación, FIXMBR, FIXBOOT, CHKDSK /R… pero no hubo forma. El disco estaba muerto y bien muerto. Así que recurrí -como ya hiciera en el pasado- al liveCD de Ubuntu y recuperé todo el disco secundario en un disco duro USB. Pero el principal, el C:, el viejo Seagate de 80 GB, ni se veía.

La opción -aparte de lamentarse, una vez más, por no haber hecho ‘backups’- pasó por reinstalar todo de nuevo. Pero en el disco de 300 GB, que pasó a ser el principal, y dejar el viejo como secundario, con todas las prevenciones del mundo.

La secuencia incluyó volver a particionar y formatear ambos discos y reinstalar todo el software, incluyendo el Sistema Operativo (XP) y todos los service packs y parches, así como el Office con los suyos. Más un montón de aplicaciones de las que no puedo prescindir. Dicho sea de paso, al equipo le ha venido bien adelgazar en ’software’, porque tenía instaladas aplicaciones de las que ni yo mismo sabía que estaban instaladas.

Después de casi tres días, tengo el equipo funcionando, y mucho más rápido que antes (por el adelgazamiento).

Lo malo es que con el disco duro se fueron todos los juegos de coches con los que juego con el Bicho Nº 1, y que habrá que ir reinstalando poco a poco. Eso sí: todos los logros y avances adquiridos se han ido al garete: hay que empezar de cero en todos y cada uno de ellos.

Dos cosas más:

1.- Esto lo estoy escribiendo en un nuevo equipo que compré el verano pasado… ¡con Windows Vista! y que está llamado a ser el futuro PC familiar, aunque de momento lo uso para familiarizarme con el Vista (cosa bastante ilógica, con el Windows 7 ya en la calle).

2.- Aunque después de particionar y formatear el disco de 80 GB el XP veía perfectamente la nueva unidad, acabo de echar un vistazo y no aparece la unidad ‘D:’ ‘Datos’.

¿Alguien conoce algún punto limpio en Madrid?

Saludos.

Carlos.


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

10 Diciembre 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.