SQL*Loader y WHEN: la palabra clave es BOUNDFILLER.

Estos días estoy generando unos ‘scripts’ de carga masiva de tablas por medio de SQL*Loader. Se trata de cargar una gran cantidad de datos en tablas que luego se utilizarán en procesos de filtrado, limpieza (lo que llaman «data-cleansing») y enriquecimiento.
El asunto es que lo primero que se hace tras cargar los datos en tablas es borrar filas en base a ciertos criterios. Como quiera que la tabla (a partir de los ficheros) tiene unos cuantos millones de filas, se me ocurrió que podía hacerse el primer filtrado en la propia carga (esto es: no cargar las filas que no cumplan dichos criterios).
El problema estaba en que los ficheros de carga eran delimitados por tabuladores, y las cláusulas ‘WHEN’ del fichero de control no se evaluaban bien en el caso de venir como nulos (dos TAB seguidos) ya que -una vez más- se cumple la máxima de la lógica trivaluada: NULL = algo ? => NULL.

En mi caso particular había que dejar fuera, entre otras, filas que vinieran con una ‘E’ en cierto campo, por lo que en el fichero de control introduje:

LOAD DATA 

APPEND INTO TABLE MI_TABLA
WHEN ( MI_CAMPO != 'E')
FIELDS TERMINATED BY X'09' 

(

...

MI_CAMPO "NVL(:mi_campo,'P')",

...

)

Pero descubrí que aparecían como descartadas (‘discarded‘) las filas que llegaban con el campo NULL (TABTAB). SQL*Loader hacía la evaluación basándose en el valor original del campo (MI_CAMPO) en vez de en el valor tratado (NVL(:mi_campo,’P’)).

Intenté después definir un campo ‘FILLER’ previo para utilizarlo como ‘dummy’ sobre el que formatear:

LOAD DATA 

APPEND INTO TABLE MI_TABLA
WHEN ( MI_CAMPO != 'E')
FIELDS TERMINATED BY X'09' 

(

...
MI_CAMPO_ FILLER,
MI_CAMPO "NVL(:mi_campo_,'P')",

...

)

pero SQL*Loader me decía que nones. Que no podía utilizar el campo FILLER MI_CAMPO_ en la expresión de MI_CAMPO.

Hasta que después de mucho leer descubrí en la documentación un oscuro y casi indocumentado parámetro llamado BOUNDFILLER. Un campo declarado BOUNDFILLER es como un campo FILLER, pero a diferencia de éstos puede ser utilizado en cadenas SQL, ya que SQL*Loader reserva espacio para ellos en el ‘bind array’.

Sabiendo esto (y después de hacer unas cuantas pruebas) fue fácil resolver el problema:

LOAD DATA 

APPEND INTO TABLE MI_TABLA
WHEN ( MI_CAMPO != 'E')
FIELDS TERMINATED BY X'09' 

(

...
MI_CAMPO_ BOUNDFILLER,
...
MI_CAMPO EXPRESSION "NVL(:mi_campo_,'P')",

...

)

y todo funcionó a las mil maravillas.

Otra utilidad que se le puede dar es, por ejemplo, en el caso de tener que componer fechas a partir de campos AÑO, MES DÍA:


(
...
DIA BOUNDFILLER,
MES BOUNDFILLER,
ANO BOUNDFILLER,
...
FECHA EXPRESSION "TO_DATE(:dia||:mes||:ano,'DDMMYYYY')"
...
)

Saludos.

Carlos.

4 Responses to SQL*Loader y WHEN: la palabra clave es BOUNDFILLER.

  1. Oscar de la Torre dice:

    No lo mencionas pero asumo que la versión de base de datos será 9i+.
    Por este tipo de cosas prefiero siempre utilizar tablas externas y manejar la transformación de datos, el filtrado y todo lo que haga falta con SQL puro (o PL/SQL si es necesario).
    De todas formas, muy interesante el artículo, nunca se sabe cuando te encontrarás con versiones más antiguas 🙂

  2. carlosal dice:

    Óscar:

    La versión es, en efecto, 10.2.0.3.

    Lo de las tablas externas era otra solución, el problema es que hay que tratar unos 100 ficheros con un total de unos 40 mllones de filas en un proceso bastante complejo que involucra además otras tablas con varios millones de filas también.

    Por ello preferí usar sqlldr en ‘direct path’ y partir de ahí.

    Por cierto: yo heredé tu puesto (tu silla, tu mesa, tu PC, tus ‘scripts’ y procesos) en cierto edificio en Pozuelo… pero de eso hace ya algún tiempo.

    Saludos.

    Carlos.

  3. Oscar de la Torre dice:

    No jorobes!!! Qué pequeño es este mundo 😀

  4. Leandro Ramírez dice:

    Muchassss gracias, necesitaba exactamente la funcionalidad que hace el BOUNDFILLER… Bien explicado

Deja un comentario