Convertir BYTE a entero (“unsigned smallint”) en Teradata.

Supongamos que queremos convertir datos de typo BYTE(2) a su valor entero. Lo primero que pensamos es en algo así como:

BTEQ -- Enter your SQL request or BTEQ command:
SELECT CAST(('327D'XB) AS INTEGER);

*** Failure 3532 Conversion between BYTE data and other types is illegal.
               Statement# 1, Info =0
*** Total elapsed time was 1 second.

Error. En Teradata los typos BYTE son bastante puñeteros, y no permiten la conversión de tipos en ninguna circunstancia (Esto es una máxima en Teradata).

Entonces ¿qué hacemos si queremos convertir un valor BYTE a su entero correspondiente? Pues que deberemos, como siempre, hacer trampas.

Teradata basa su paralelismo y su necesaria distribución de los datos en unas funciones de ‘hashing‘ que son las encargadas de distribuir las filas en los AMPs basándose en los valores de las columnas definidas en los ‘primary indexes‘. Además ofrece al usuario tres funciones de utilidad para poder analizar dicha distribución: HASHROW, HASHBUCKET y HASHAMP. Normalmente se utilizan juntas, ya que la salida de una suele ser la entrada de la otra:

SELECT HASHAMP(HASHBUCKET(HASHROW('A')));

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

HASHAMP(HASHBUCKET(HASHROW('A')))
---------------------------------
                                4

Esto quiere decir que si tuviéramos una tabla cuyo ‘primary index‘ fuese una columna CHAR(1), una fila cuyo valor para esa columna fuese ‘A’ estaría localizada en el AMP número 4 (esto depende de la configuración de cada sistema y sería algo largo explicar aquí todo el mecanismo, el que quiera puede repasarse la documentación).

Nosotros nos vamos a centrar en la segunda función “HASHBUCKET”, que recibe un BYTE(4) como resultado de la salida de “HASHROW” y devuelve un entero de cuatro bytes. Los dos primeros bytes se llaman DSW (‘destination selection word‘) y aquí viene el truco: esta DSW no es más que un valor entre 0 y 65535 que se utilizará luego en el “HASHMAP”, pero esa es otra historia…

El caso es que si tenemos un valor de hasta 2 bytes (‘1A9F’, por ejemplo) podemos resolver su valor entero haciendo esto:

 BTEQ -- Enter your SQL request or BTEQ command:
SELECT HASHBUCKET('1A9F0000'XB);

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

HASHBUCKET('1A9F0000'XB)
------------------------
                    6815

Y devuelve 6815 que es, efectivamente, el valor entero de ‘1A9F’.

Nótese que hemos tenido que rellenar a ceros hasta conseguir un BYTE(4), que es lo que pide HASHBUCKET(), pero nos permite un ‘CAST’ que de otra manera está prohibido.

Pero aún podemos ir un poco más lejos:

 BTEQ -- Enter your SQL request or BTEQ command:
CREATE TABLE MY_DB.PRUEBA01(ID_N INTEGER NOT NULL,
                            B_N2 BYTE(2) NOT NULL
                            )
       UNIQUE PRIMARY INDEX (ID_N)
;

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

 BTEQ -- Enter your SQL request or BTEQ command:

INSERT INTO MY_DB.PRUEBA01(ID_N, B_N2) VALUES (1, '0001'XB);

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

 BTEQ -- Enter your SQL request or BTEQ command:
INSERT INTO MY_DB.PRUEBA01(ID_N, B_N2) VALUES (2, '0002'XB);

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

 BTEQ -- Enter your SQL request or BTEQ command:
INSERT INTO MY_DB.PRUEBA01(ID_N, B_N2) VALUES (10, '000A'XB);

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

 BTEQ -- Enter your SQL request or BTEQ command:
INSERT INTO MY_DB.PRUEBA01(ID_N, B_N2) VALUES (255, '00FF'XB);

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

 BTEQ -- Enter your SQL request or BTEQ command:
INSERT INTO MY_DB.PRUEBA01(ID_N, B_N2) VALUES (65535, 'FFFF'XB);

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

 BTEQ -- Enter your SQL request or BTEQ command:

SELECT * FROM MY_DB.PRUEBA01 ORDER BY 1
;

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

       ID_N  B_N2
-----------  ----
          1  0001
          2  0002
         10  000A
        255  00FF
      65535  FFFF

Tenemos una tabla con valores BYTE(2) y sus enteros correspondientes (sólo a efectos de demostración).

Podemos entonces convertir los BYTE(2) a enteros entre 0 y 65535 haciendo:

 BTEQ -- Enter your SQL request or BTEQ command:
SELECT ID_N, B_N2, HASHBUCKET(B_N2||'0000'XB)
  FROM MY_DB.PRUEBA01
 ORDER BY 1;

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

       ID_N  B_N2  HASHBUCKET((B_N2||'0000'XB))
-----------  ----  ----------------------------
          1  0001                             1
          2  0002                             2
         10  000A                            10
        255  00FF                           255
      65535  FFFF                         65535

et voilà! Aprovechando que podemos ‘concatenar bytes’ utilizando la expresión “||’0000’XB”, conseguimos rellenar a ceros los dos bytes restantes que espera “HASHBUCKET” y realizar la conversión prohibida.

Nota importante: Esta entrada se escribió tomando como base un sistema con 65.536 hash buckets. Si tu sistema está configurado para 1.048.576 hash buckets debes leer esto.

Saludos.

Carlos.

3 respuestas a Convertir BYTE a entero (“unsigned smallint”) en Teradata.

  1. […] viendo claro. Sólamente con conocer como funciona SUBSTR() con datos tipo ‘BYTE’ y cómo convertir estos datos a ‘INTEGERs’ podemos hacer la […]

  2. […] Ya hemos hablado otras veces del antipático tipo BYTE en Teradata y de los problemas que encontramos en las conversiones de y hacia ese tipo de dato. Hemos incluso recurrido a trampas de prestidigitador barato para conseguir conversiones entre BYTE e INTEGER. […]

  3. […] esos cuatro bits extra (SHR -shift right- ¿recuerda alguien el ensamblador?): para utilizar la conversión de BYTE a INTEGER en estos sistemas hay que dividir por 16 (cuatro SHR: […]

Deja un comentario

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: