Zapas Nuevas (y van…) 

31 marzo \31\UTC 2017

Zapas Nuevas: Mizuno Wave Inspire.

Mizuno Wave Inspire

Mizuno Wave Inspire

Segunda experiencia con unas Mizuno.

Saludos.

Carlos.

Anuncios

Ahí va ese bólido…

15 marzo \15\UTC 2017

Saludos.

Carlos.


NUMBERs, JOINs y ORs…

2 marzo \02\UTC 2017

Desde la versión 14, Teradata implementa el tipo “Oracle” NUMBER. Y lo hace a su imagen y semejanza: con el almacenamiento en forma de exponente y mantisa. El motivo es, principalmente, facilitar las migraciones desde Bases de Datos Oracle a Teradata (de las que he visto unas cuantas y he participado en otras). Anteriormente, al no disponer Teradata de un tipo análogo al NUMBER de Oracle, había que elegir entre los numéricos “nativos”, con las dificultades que ello conlleva y la propensión a errores por elecciones incorrectas.

El tipo en sí presenta ciertas ventajas y varias particularidades, como el espacio de almacenamiento variable (de 2 a 18 bytes), la posibilidad de variar escala y precisión sin modificar las filas o la precisión de 18 dígitos (o incluso más) en las operaciones con ellos. Por otra parte, es un tipo en coma flotante (floating point) que puede no ser todo lo exacto que necesitamos y puede llevar a pequeñas incorrecciones en los cálculos.

Así que, en teoría, no hay ningún problema para utilizar NUMBER(n, m) en Teradata tal como lo hacemos en Oracle, donde es el tipo numérico por excelencia.

Pero a veces las cosas no son lo que parecen:

 BTEQ -- Enter your SQL request or BTEQ command:
SELECT * FROM DBC.DBCINFO;


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

InfoKey                        InfoData
------------------------------ --------------------------------------
RELEASE                        15.10.02.01
VERSION                        15.10.02.03
LANGUAGE SUPPORT MODE          Standard

 BTEQ -- Enter your SQL request or BTEQ command:


CREATE MULTISET TABLE TESTDB.TEST1 (
   ID_C CHAR(2) NOT NULL,
   ID_N NUMBER
)
PRIMARY INDEX (ID_C);


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


 BTEQ -- Enter your SQL request or BTEQ command:


INSERT INTO TESTDB.TEST1 VALUES ('98', 999999999999998);


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


 BTEQ -- Enter your SQL request or BTEQ command:
INSERT INTO TESTDB.TEST1 VALUES ('99', 999999999999999);


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


 BTEQ -- Enter your SQL request or BTEQ command:


CREATE MULTISET TABLE TESTDB.TEST2 (
   ID_C CHAR(2) NOT NULL,
   ID_N NUMBER(15, 0),
   C_TXT CHAR(6)
)
PRIMARY INDEX (ID_C);


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


 BTEQ -- Enter your SQL request or BTEQ command:


INSERT INTO TESTDB.TEST2 VALUES ('98', 999999999999998, 'OCHO');


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


 BTEQ -- Enter your SQL request or BTEQ command:
INSERT INTO TESTDB.TEST2 VALUES ('99', 999999999999999, 'NUEVE');


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


 BTEQ -- Enter your SQL request or BTEQ command:


SELECT c.C_TXT
      ,a.ID_C
      ,a.ID_N
  FROM ( SELECT ID_C
               ,ID_N
          FROM TESTDB.TEST1
         WHERE ( ID_C = '98' AND
                 ID_N = 999999999999998 )
            OR ( ID_C = '99' AND
                 ID_N = 999999999999999 )
       ) a
  LEFT JOIN TESTDB.TEST2 c
         ON ( a.ID_C = c.ID_C
          AND a.ID_N = c.ID_N )
;


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

C_TXT   ID_C                                      ID_N
------  ----  ----------------------------------------
(null)  99                             999999999999999
OCHO    98                             999999999999998

 BTEQ -- Enter your SQL request or BTEQ command:


SELECT c.C_TXT
      ,a.ID_C
      ,a.ID_N
  FROM ( SELECT ID_C
               ,ID_N
          FROM TESTDB.TEST1
         WHERE ( ID_C = '99' AND
                 ID_N = 999999999999999 )
            OR ( ID_C = '98' AND
                 ID_N = 999999999999998 )
       ) a
  LEFT JOIN TESTDB.TEST2 c
         ON ( a.ID_C = c.ID_C
          AND a.ID_N = c.ID_N )
;


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

C_TXT   ID_C                                      ID_N
------  ----  ----------------------------------------
(null)  98                             999999999999998
NUEVE   99                             999999999999999

 BTEQ -- Enter your SQL request or BTEQ command:

Como podemos ver, la misma query da resultados diferentes sólo con cambiar el orden de los filtros en la parte WHERE … OR de la subselect “a”.

¡Pero eso es imposible! Todos sabemos que el ‘OR’ es conmutativo.

Además, ambos resultados son erróneos.

¿Y qué puede causar este comportamiento? Pues, presumiblemente, los resultados intermedios de la subselect “a” varían en cuanto al tipo elegido según lleguen las primeras filas como 999999999999998 ó 999999999999999. El tipo definido para ID_N en TEST1 es NUMBER, sin especificar escala ni precisión, mientras que en TEST2 es NUMBER(15, 0). Aunque externamente “NUMBER es NUMBER”, en algún lugar se producen diferencias que hacen que el JOIN no funcione como se espera.

Por eso, si estamos en lo cierto, al hacer un CAST explícito sobre las columnas NUMBER en la subselect todo debería funcionar OK:

SELECT c.C_TXT
      ,a.ID_C
      ,a.ID_N
  FROM ( SELECT ID_C
               ,ID_N (NUMBER(15,0))  --CAST!!
          FROM TESTDB.TEST1
         WHERE ( ID_C = '98' AND
                 ID_N = 999999999999998 )
            OR ( ID_C = '99' AND
                 ID_N = 999999999999999 )
       ) a
  LEFT JOIN TESTDB.TEST2 c
         ON ( a.ID_C = c.ID_C
          AND a.ID_N = c.ID_N )
;


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

C_TXT   ID_C              ID_N
------  ----  ----------------
NUEVE   99     999999999999999
OCHO    98     999999999999998

 BTEQ -- Enter your SQL request or BTEQ command:


SELECT c.C_TXT
      ,a.ID_C
      ,a.ID_N
  FROM ( SELECT ID_C
               ,ID_N (NUMBER(15,0))  --CAST!!
          FROM TESTDB.TEST1
         WHERE ( ID_C = '99' AND
                 ID_N = 999999999999999 )
            OR ( ID_C = '98' AND
                 ID_N = 999999999999998 )
       ) a
  LEFT JOIN TESTDB.TEST2 c
         ON ( a.ID_C = c.ID_C
          AND a.ID_N = c.ID_N )
;


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

C_TXT   ID_C              ID_N
------  ----  ----------------
NUEVE   99     999999999999999
OCHO    98     999999999999998

 BTEQ -- Enter your SQL request or BTEQ command:

En efecto, vemos que con CAST explícito todo vuelve a la normalidad y la SELECT da el resultado esperado (y correcto esta vez).

Claro que esto no deja de ser un “workaround“. Supongo que alguien tiene trabajo en Rancho Bernardo.

Por otra parte, siempre se debería especificar escala y precisión en los tipos NUMBER (en Oracle también).

Saludos.

Carlos.


El nuevo piano.

24 febrero \24\UTC 2017

La Parte Contraria ha comprado un nuevo piano. Es mucho mejor que el que teníamos, dice. Alemán.

El nuevo piano

El nuevo piano

Pero yo echaré de menos el viejo y querido Petrof…
Saludos.

Carlos.


Cargar campos “COBOL BINARY” con Multiload.

22 febrero \22\UTC 2017

Análogamente a los tipos COBOL ‘packed decimal’ que corresponden con tipos SQL DECIMAL(n, m), los tipos BINARY corresponden a tipos enteros SQL: SMALLINT (2 bytes), INTEGER (4 bytes) y BIGINT (8 bytes).

En multiload, definiendo los .FIELD adecuados en el .LAYOUT conseguiremos una carga sin errores:

...
.LAYOUT THE_LAYOUT;
...
.FIELD binary_field2  SMALLINT;
.FIELD binary_field4  INTEGER;
.FIELD binary_field8  BIGINT;
...

Pero ¿qué ocurre cuando el ‘PIC’ del campo BINARY indica que tiene decimales? Como en el caso de los ‘packed decimals‘ los decimales no se guardan en los campos BINARY, sino que se resuelven con los ‘PICTURES’ (PIC). Así pues, nos podemos encontrar, por ejemplo, un BINARY con un PIC S9(3)V9(6).
A diferencia de con los ‘packed decimals’ o los BINARY enteros, no podemos apoyarnos en la funcionalidad de multiload para la conversión directa, ya que no hay un tipo ‘SQL’ equivalente para BINARY con decimales.

Por eso lo debemos solucionar en dos pasos:

Primero definimos el .FIELD adecuado en el .LAYOUT. Si el campo es, por ejemplo, BINARY PIC S9(3)V9(6) entonces ocupará 4 bytes, y por tanto corresponderá a un tipo INTEGER. Así pues, en el ‘script‘ de multiload:

...
.LAYOUT THE_LAYOUT;
...
.FIELD binary_field4  INTEGER;
...

(Recordemos que según el tamaño del campo deberíamos haberlo definido como SMALLINT, INTEGER o BIGINT).

Una vez hecho esto deberemos insertarlo en la columna de la tabla correspondiente (que deberá ser DECIMAL(9, 6)). Es aquí donde haremos el CAST (implícito).

Más abajo en el mismo ‘script‘ de multiload, haciendo:

...
INSERT INTO MY_DB.MY_TABLE( ..., MY_COLUMN, ...) 
VALUES(..., :binary_field4/1000000.000000, ...);
...

conseguimos que el :binary_field4, que hemos cargado como INTEGER según el .LAYOUT se almacene en la columna como DECIMAL(9, 6) obedeciendo al ‘PIC’. Es muy importante definir el denominador con el número de decimales adecuado (en nuestro caso seis), porque si no se producirá un CAST erróneo que provocará truncamiento y nos dará un resultado incorrecto.

Saludos.

Carlos.


Se ha muerto John Wetton

31 enero \31\UTC 2017

Se ha muerto John Wetton. Cantante y bajista de los mejores King Crimson.

Además, la voz inconfundible de una de las canciones de mi vida:

You make my life and times a book of bluesy saturday…

Saludos.

Carlos.


zypper y la actualización obstinada.

12 enero \12\UTC 2017

Software Updates” en mi openSuSE 42.1 Leap me avisaba de una actualización pendiente:

“openSuSE-2016-1453 Security update for Chromium”

Pero cada vez que intentaba la actualización se producía un fallo y me indicaba que los detalles del problema estaban en el “detailed report“. Report que, por otra parte, fui incapaz de encontrar. Y así una y otra vez.

Así que, harto del dichoso mensajito de error, me fui a la documentación: zypper.

Gestión de actualizaciones:
...
patch                   Instalar los parches necesarios.

Con esto:

carlos@OpenSuSE42:~> sudo zypper patch
root's password:
Cargando datos del repositorio...
Leyendo los paquetes instalados...
Resolviendo dependencias de paquete...

Problema: patch:openSUSE-2016-1453-1.noarch está en conflicto con chromium.x86_64 < 55.0.2883.75-99.2 
          proporcionado por chromium-54.0.2840.100-91.1.x86_64 
 Solución 1: instalar chromium-55.0.2883.75-99.2.x86_64 (con cambio de proveedor) http://packman.links2linux.de -->  openSUSE
 Solución 2: no instalar patch:openSUSE-2016-1453-1.noarch

Choose from above solutions by number or cancel [1/2/c] (c): 1
Resolviendo dependencias...
Resolviendo dependencias de paquete...

The following application is going to be installed:
  Chromium

The following 3 NEW packages are going to be installed:
  libre2-0 libsnappy1 libwebpdemux1

The following NEW patch is going to be installed:
  openSUSE-2016-1453

The following package is going to be REMOVED:
  chromium-ffmpeg

The following package is going to be upgraded:
  chromium

1 package to upgrade, 3 new, 1 to remove.
Tamaño total de descarga: 50,3 MiB. Ya en caché: 0 B. Después de la operación, se liberarán 900,2 KiB.
¿Desea continuar? [s/n/? mostrar todas las opciones] (s): s
Recuperando package libsnappy1-1.1.3-1.1.x86_64            (1/4),  19,8 KiB ( 33,0 KiB desempaquetado)
Recuperando: libsnappy1-1.1.3-1.1.x86_64.rpm ..................................[TERMINADO (1,9 KiB/s)]
Recuperando package libre2-0-20161101-2.1.x86_64           (2/4), 143,4 KiB (411,9 KiB desempaquetado)
Recuperando: libre2-0-20161101-2.1.x86_64.rpm ...............................[TERMINADO (286,7 KiB/s)]
Recuperando package libwebpdemux1-0.4.3-7.1.x86_64         (3/4),  13,8 KiB ( 14,1 KiB desempaquetado)
Recuperando: libwebpdemux1-0.4.3-7.1.x86_64.rpm ...........................................[TERMINADO]
Recuperando package chromium-55.0.2883.75-99.2.x86_64      (4/4),  50,1 MiB (193,3 MiB desempaquetado)
Recuperando: chromium-55.0.2883.75-99.2.x86_64.rpm ..........................[TERMINADO (610,3 KiB/s)]
Buscando conflictos de archivos: ..........................................................[TERMINADO]
(1/4) Instalando: libsnappy1-1.1.3-1.1.x86_64 .............................................[TERMINADO]
(2/4) Instalando: libre2-0-20161101-2.1.x86_64 ............................................[TERMINADO]
(3/4) Instalando: libwebpdemux1-0.4.3-7.1.x86_64 ..........................................[TERMINADO]
(4/4) Instalando: chromium-55.0.2883.75-99.2.x86_64 .......................................[TERMINADO]
carlos@OpenSuSE42:~> 

Una vez hecho esto, la famosa actualización “openSuSE-2016-1453 Security update for Chromium” no me ha vuelto a molestar…

He aquí una pequeña magia de zypper: te detecta los problemas, te indica las soluciones a elegir y ejecuta la que tú le digas.

Saludos.

Carlos.