Las Bases de Datos actuales -y más las Data Warehouses- almacenan ya no sólo datos de los tipos básicos (caracteres, números, fechas) sino que cada vez más son requeridas para almacenar datos heterogéneos: documentos (.pdf, .doc …), imágenes, sonidos, etc…
Teradata -como casi todos los SGBDRs- maneja datos binarios grandes (BLOBs -Binary Large OBjects-) y es capaz de almacenarlos de forma eficiente. La documentación invita a utilizar estas capacidades, pero aparece la eterna pregunta: ¿Cómo cargo esos datos en la Base de Datos?
Las utilidades Teradata ofrecen pocas alternativas: las únicas que son capaces de llevar a cabo la tarea son ‘SQL Assistant’ y ‘bteq’, y ambas con funcionalidad limitada.
Así que si se necesita una solución rápida y fácil para incorporar los datos binarios, enseguida se piensa en Windows Scripting Host y Visual Basic Script por su simplicidad y facilidad de programación. (He de decir que, aunque no soy ningún fan de Microsoft, encuentro que el WSH es una muy buena herramienta)
No nos podemos olvidar del acceso a la base de datos: personalmente me gusta OLEDB, con lo que una instalación del Teradata OLEDB provider es necesaria. Utilizaremos ADO como capa de acceso a la base de Datos.
Con todos estos ingredientes podemos comenzar la tarea: cargar imágenes (pero podría ser cualquier otro tipo de datos binario) a partir de ficheros .jpg en Teradata.
Lo primero, vamos a crear una tabla que contenga, por ejemplo, el nombre del fichero y la imagen como columna BLOB:
BTEQ -- Enter your DBC/SQL request or BTEQ command:
CREATE SET TABLE MY_DATABASE.PRUEBA_BLOB,
NO FALLBACK,
NO JOURNAL
(
NOM_FICHERO VARCHAR(64) NOT NULL CHARACTER SET LATIN,
BLOB_IMAGE BLOB(2M) NULL
)
UNIQUE PRIMARY INDEX (NOM_FICHERO)
;
*** Table has been created.
*** Total elapsed time was 1 second.
BTEQ -- Enter your DBC/SQL request or BTEQ command:
La columna BLOB se puede dejar sin tamaño, pero esto hará que tome el valor por defecto: 2 Gb. ¿Un poco excesivo, no? Nos conformamos con unas muy generosas 2 megas por imagen.
Con la tabla creada nos sumergimos en el mundo del Visual Basic Script:
Const CONNECTIONSTRING = "Provider=TDOLEDB;Data Source=MY_DW;
User Id=myuser;password=mypass;" 'OLEDB
Dim vNombreFichero
'Conexión
Dim objConnection
Main
Sub Main
If WScript.Arguments.Length 1 Then
vNombreFichero = InputBox("Introduzca el nombre del fichero a tratar:", "PruebaBLOB.vbs")
If vNombreFichero = "" Or IsNull(vNombreFichero) Then
WScript.Echo "ERROR: Falta fichero a tratar." & Chr(13) & Chr(10) & Chr(13) & Chr(10) & "(Uso: PruebaBLOB.vbs )"
WScript.Quit
End If
Else
vNombreFichero = WScript.Arguments(0)
End If
Wscript.Echo "Let's roll..."
AbrirConexion
CerrarConexion
Wscript.Echo "Bye."
End Sub
Sub AbrirConexion
Set objConnection = WScript.CreateObject("ADODB.Connection")
objConnection.ConnectionString = CONNECTIONSTRING
objConnection.Open()
Wscript.Echo "Conectando a Teradata..."
If Err.Number 0 Then
Wscript.Echo "Err# " & Err.Number & " - " & Err.Description
Else
Wscript.Echo "Connected to database as " & objConnection.Properties("User Name") & " en " & objConnection.Properties("Server Name") & "."
End If
End Sub
Sub CerrarConexion
objConnection.Close()
Set objConnection = Nothing
End Sub
Esto no es más que probar la conexión -abrir y cerrar- y una comprobación básica de que debe llegar el nombre del fichero como argumento:
C:\Procesos\CargarBLOBs>cscript PruebaBLOB.vbs foto1.jpg Microsoft (R) Windows Script Host versión 5.6 Copyright (C) Microsoft Corporation 1996-2001. Reservados todos los derechos. Let's roll... Conectando a Teradata... Connected to database as myuser en MY_DW. Bye. C:\Procesos\CargarBLOBs>
Así pues, todo parece funcionar (‘so far, so good…‘)
Lo bueno empieza ahora. La primera idea que nos viene a la cabeza es utilizar los FileSystemObjects del WSH para abrir el fichero, leerlo e insertarlo en la base de datos. Pero la cosa no va por ahí: el manejo de ficheros que se proporciona en el WSH y sus ’streams’ están pensados para trabajar únicamente en modo texto, por lo que no nos sirven de mucho.
Los tiros van por otro lado: Las últimas versiones de ADO incorporan un objeto que permite trabajar con datos binarios: ‘ADODB.Stream’.
En efecto, con objetos ADODB.Stream podemos manipular ficheros binarios:
Sub InsertarImagen
'Cargar BLOB con fichero
Const adTypeBinary = 1
'Create Stream object
Dim inStream
Dim vBuffer
Set inStream = CreateObject("ADODB.Stream")
inStream.Open
inStream.type=adTypeBinary
Wscript.Echo "Cargando Fichero Imagen..."
inStream.LoadFromFile vNombreFichero
Wscript.Echo "Tamaño de la imagen: " & inStream.Size
...
Para ver que la cosa funciona bastará hacer una prueba y verificar que inStream.Size da el tamaño del fichero de imagen:
C:\Procesos\CargarBLOBs>cscript PruebaBLOB.vbs foto1.jpg Microsoft (R) Windows Script Host versión 5.6 Copyright (C) Microsoft Corporation 1996-2001. Reservados todos los derechos. Let's roll... Conectando a Teradata... Connected to database as myuser en MY_DW. Cargando Fichero Imagen... Tamaño de la imagen: 710312 Bye. C:\Procesos\CargarBLOBs>
Así pues, vemos que el objeto ADO.Stream funciona según lo esperado.
Siguiente asunto: ¿qué tipo de dato ADO utilizamos para ‘mapear’ el dato BLOB? Esta es fácil: la documentación de ADO lo indica claramente: LongVarBinary.
Aquí hay dos cosas a tener en cuenta: Primero, en ADO todos los parámetros de longitud variable necesitan un argumento de tamaño en la creación del objeto:
Si hacemos:
Set objParm2 = objCommand.CreateParameter("blob_image", adLongVarBinary,
adParamInput)
objCommand.Parameters.Append objParm2
Tendremos que:
C:\Procesos\CargarBLOBs\PruebaBLOB.vbs(157, 4) ADODB.Parameters: Objeto Parameter mal definido. Se proporcionó información incompleta o incoherente.
Por lo que hay que definir el parámetro con tamaño:
Set objParm2 = objCommand.CreateParameter("blob_image", adLongVarBinary,
adParamInput, 2097152)
objCommand.Parameters.Append objParm2
La segunda cuestión a recordar es que debemos asignar el valor del parámetro no con ‘Value’, sino con AppendChunk.
objParm2.AppendChunk inStream.Read
Teniendo en cuenta estas dos cosas podemos ya ejecutar un ‘ADO.Command’ con el ‘INSERT’:
...
Dim objCommand
Dim sCommandText
Dim objParm1,objParm2
sCommandText = "INSERT INTO MY_DATABASE.PRUEBA_BLOB(NOM_FICHERO, BLOB_IMAGE) " & _
"VALUES ( ?, ? )"
Set objCommand = Wscript.CreateObject("ADODB.Command")
objCommand.CommandType = adCmdText
objCommand.CommandText = sCommandText
'Command Prepared
objCommand.Prepared = True
objCommand.ActiveConnection = objConnection
'Parámetros:
Set objParm1 = objCommand.CreateParameter("nom_fichero", adVarChar,
adParamInput, 64 )
objCommand.Parameters.Append objParm1
objParm1.Value = vNombreFichero
Set objParm2 = objCommand.CreateParameter("blob_image", adLongVarBinary,
adParamInput, 2097152) 'inStream.Size
objCommand.Parameters.Append objParm2
objParm2.AppendChunk inStream.Read
objCommand.Execute
'Close the stream
inStream.Close()
Set inStream = Nothing
Set objCommand = Nothing
End Sub
Ejecutamos:
C:\Procesos\CargarBLOBs>cscript PruebaBLOB.vbs foto1.jpg Microsoft (R) Windows Script Host versión 5.6 Copyright (C) Microsoft Corporation 1996-2001. Reservados todos los derechos. Let's roll... Conectando a Teradata... Connected to database as myuser en MY_DW. Cargando Fichero Imagen... Tamaño de la imagen: 710312 C:\Procesos\CargarBLOBs\PruebaBLOB.vbs(161, 4) OLE DB Provider for Teradata: [NCR] Invalid precision: cbColDef value out of range
¡ERROR! ¿Por qué? Hemos hecho todo según la documentación. ¿Se nos ha pasado algo?
Sí. La respuesta está en la documentación de Teradata: ‘OLE DB Provider for Teradata’. En un lugar recóndito se nos dice que hay que habilitar el tratamiento de LOBs mediante las ‘extended properties’ del OLEDB provider:
Const CONNECTIONSTRING = "Provider=TDOLEDB;Data Source=MY_DW; User Id=myuser;password=mypass; Extended Properties=""EnableLOBSupport=Yes;"";" 'OLEDB EnableLOBSupport
Y ahora todo va a ir OK:
C:\Procesos\CargarBLOBs>cscript PruebaBLOB.vbs foto1.jpg Microsoft (R) Windows Script Host versión 5.6 Copyright (C) Microsoft Corporation 1996-2001. Reservados todos los derechos. Let's roll... Conectando a Teradata... Connected to database as myuser en MY_DW. Cargando Fichero Imagen... Tamaño de la imagen: 710312 Bye. C:\Procesos\CargarBLOBs>
Y vemos que la inserción se realizó:
BTEQ -- Enter your DBC/SQL request or BTEQ command: SELECT * FROM MY_DATABASE.PRUEBA_BLOB; SELECT * FROM MY_DATABASE.PRUEBA_BLOB; *** Query completed. One row found. 2 columns returned. *** Total elapsed time was 1 second. NOM_FICHERO BLOB_IMAGE ------------ ------------------------------------------------------- foto1.jpg FFD8FFE000104A46494600010101012C012C0000FFFE0029433A5C5
Evidentemente, BTEQ sólo nos muestra el contenido hexadecimal de la imagen. Si queremos ver la foto deberemos utilizar el ‘SQL Assisstant’ o algún otro ’software’, pero la imagen está ahí. Y eso era lo que queríamos.
Una última cosa: el ejemplo mostrado aquí es fácilmente portable a Visual Basic. Sólo hay que tener en cuenta las diferencias que existen entre VB y VBScript.
Saludos.
Carlos.
27 Febrero 2009 a las 19:03 |
Thanks! This is perfect. Do you have an example of reading the file using VBS? I keep getting an error saying “LOBs are not allowed to be selected in Record or Indicator modes”
(Used Google Translate)
Gracias! Esto es perfecto. ¿Tiene usted un ejemplo de lectura usando el archivo VBS? Sigo recibiendo un error diciendo que “LOBs no pueden ser seleccionados en el Registro o Indicador de modos”
27 Febrero 2009 a las 20:31 |
I’m not sure about what you mean. You can read the file with ADO.Stream as explained.
Cheers.
Carlos.