Pasando el testigo

8 octubre \08\UTC 2014

No, yo no estaré en el próximo Rallye de Ávila. Pero mi casco, mi mono y mi HANS sí.

Unos (jóvenes) amigos se han liado la manta a la cabeza y van a debutar en el mundo de los rallyes. Como andan un poco justos de material y dinero nos han pedido que les prestemos los “trastos”:

Casco, mono y HANS

Casco, mono y HANS

No sé si algún día volveré a usarlos en una competición, pero todo parece indicar que esto es lo que llaman “pasar el testigo”…

Saludos.

Carlos.


¿Cuántas tablas ves aquí?

7 octubre \07\UTC 2014

A veces, hasta el SQL más sencillo puede despistar a un observador poco avispado (“basado en hechos reales“).

 BTEQ -- Enter your SQL request or BTEQ command:
CREATE MULTISET TABLE CARLOS.PRUEBA01,
     NO FALLBACK ,
     NO BEFORE JOURNAL,
     NO AFTER JOURNAL,
     CHECKSUM = DEFAULT,
     DEFAULT MERGEBLOCKRATIO
     (
        ID_N  INTEGER     NOT NULL,
        C_TXT VARCHAR(15) NOT NULL
     )
     PRIMARY INDEX ( ID_N )
;


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


 BTEQ -- Enter your SQL request or BTEQ command:


INSERT INTO CARLOS.PRUEBA01 VALUES (1,'UNO');


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


 BTEQ -- Enter your SQL request or BTEQ command:
INSERT INTO CARLOS.PRUEBA01 VALUES (2,'DOS');


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


 BTEQ -- Enter your SQL request or BTEQ command:
INSERT INTO CARLOS.PRUEBA01 VALUES (3,'TRES');


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


 BTEQ -- Enter your SQL request or BTEQ command:


CREATE MULTISET TABLE CARLOS.PRUEBA02,
     NO FALLBACK ,
     NO BEFORE JOURNAL,
     NO AFTER JOURNAL,
     CHECKSUM = DEFAULT,
     DEFAULT MERGEBLOCKRATIO
     (
        ID_N  INTEGER     NOT NULL,
        C_TXT VARCHAR(15) NOT NULL
     )
     PRIMARY INDEX ( ID_N )
;


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


 BTEQ -- Enter your SQL request or BTEQ command:


INSERT INTO CARLOS.PRUEBA02 VALUES (1,'RAS');


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


 BTEQ -- Enter your SQL request or BTEQ command:
INSERT INTO CARLOS.PRUEBA02 VALUES (2,'DVA');


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


 BTEQ -- Enter your SQL request or BTEQ command:
INSERT INTO CARLOS.PRUEBA02 VALUES (3,'TRI');


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

Y ahora la ‘SELECT’ a la que hace referencia el título: “¿Cuántas tablas ves aquí?”

SELECT *
FROM CARLOS.PRUEBA01 t1,
CARLOS.PRUEBA02 t2
WHERE t1.ID_N = CARLOS.PRUEBA02.ID_N
;

La respuesta es clara ¿verdad?: Dos tablas.

Pues vamos a ver qué ocurre si ejecutamos la ‘query':

 BTEQ -- Enter your SQL request or BTEQ command:


SELECT *
  FROM CARLOS.PRUEBA01 t1,
       CARLOS.PRUEBA02 t2
 WHERE t1.ID_N = CARLOS.PRUEBA02.ID_N
;


 *** Query completed. 9 rows found. 4 columns returned.
 *** Total elapsed time was 1 second.

       ID_N  C_TXT                   ID_N  C_TXT
-----------  ---------------  -----------  ---------------
          2  DOS                        3  TRI
          2  DOS                        2  DVA
          2  DOS                        1  RAS
          3  TRES                       3  TRI
          3  TRES                       2  DVA
          3  TRES                       1  RAS
          1  UNO                        3  TRI
          1  UNO                        2  DVA
          1  UNO                        1  RAS

¿Qué es este resultado? ¿Cómo es posible que falle un ‘join’ tan sencillo?

Pues la respuesta a esta pregunta y a la que da nombre a este ‘post’ es que en realidad hay TRES tablas en el ‘join’, ya que al incluir el nombre completo de la segunda tabla en el ‘WHERE’ (t1.ID_N = CARLOS.PRUEBA02.ID_N) estamos implícitamente incluyendo la tabla PRUEBA02 una segunda vez y provocando un CROSS JOIN que es el causante de los resultados tan extraños (algo parecido a esto lo vimos aquí hace tiempo).

Por supuesto que todo se soluciona si en el ‘WHERE’ hacemos referencia a la columna de PRUEBA02 mediante el alias que hemos declarado (t2):

 BTEQ -- Enter your SQL request or BTEQ command:
 SELECT *
  FROM CARLOS.PRUEBA01 t1,
       CARLOS.PRUEBA02 t2
 WHERE t1.ID_N = t2.ID_N;


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

       ID_N  C_TXT                   ID_N  C_TXT
-----------  ---------------  -----------  ---------------
          3  TRES                       3  TRI
          2  DOS                        2  DVA
          1  UNO                        1  RAS

La moraleja del cuento: o usamos nombres cualificados completos o utilizamos alias, pero mejor no mezclarlos…

Saludos.

Carlos.


La Estrella

2 octubre \02\UTC 2014

Te amaré como a la única estrella que aún brilla sobre mi balcón.

Un balcón en Madrid…

Saludos.

Carlos.


Sorpresas con comparaciones en columnas TIMESTAMP WITH DEFAULT

18 septiembre \18\UTC 2014

A veces te encuentras sorpresas en el día a día. Hoy di con algo sorprendente respecto de las comparaciones con timestamps:

Vamos a hacer una pequeña prueba de comparaciones de TIMESTAMPs con mayor estricto para colunas definidas con ‘WITH DEFAULT':

 BTEQ -- Enter your SQL request or BTEQ command:
CREATE MULTISET TABLE CARLOS.PRUEBA01
   ( ID_N INTEGER NOT NULL,
     TS_TIMESTAMP TIMESTAMP(0) NOT NULL WITH DEFAULT )
;


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


 BTEQ -- Enter your SQL request or BTEQ command:


INSERT INTO CARLOS.PRUEBA01(ID_N) VALUES ( 1 )
;INSERT INTO CARLOS.PRUEBA01(ID_N) VALUES ( 2 )
;INSERT INTO CARLOS.PRUEBA01(ID_N) VALUES ( 3 )
;


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

 *** Insert completed. One row added.

 *** Insert completed. One row added.

 BTEQ -- Enter your SQL request or BTEQ command:


SELECT *
  FROM CARLOS.PRUEBA01
;


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

       ID_N         TS_TIMESTAMP
-----------  -------------------
          3  2014-09-18 18:27:58
          2  2014-09-18 18:27:58
          1  2014-09-18 18:27:58

 BTEQ -- Enter your SQL request or BTEQ command:
INSERT INTO CARLOS.PRUEBA01(ID_N) VALUES ( 4 );


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


 BTEQ -- Enter your SQL request or BTEQ command:


SELECT *
  FROM CARLOS.PRUEBA01
;


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

       ID_N         TS_TIMESTAMP
-----------  -------------------
          3  2014-09-18 18:27:58
          4  2014-09-18 18:28:52
          2  2014-09-18 18:27:58
          1  2014-09-18 18:27:58

 BTEQ -- Enter your SQL request or BTEQ command:
SELECT *
  FROM CARLOS.PRUEBA01
 WHERE TS_TIMESTAMP > TIMESTAMP '2014-09-18 18:27:58'
;


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

       ID_N         TS_TIMESTAMP
-----------  -------------------
          4  2014-09-18 18:28:52

Todo perfecto: Las filas reciben el CURRENT_TIMESTAMP(0) para la columna TS_TIMESTAMP y la comparación en la ‘query’ final sólo devuelve una fila estrictamente mayor a TIMESTAMP ‘2014-09-18 18:27:58′ (como debe ser).

Ahora empieza lo divertido:

Vamos a crear la misma tabla sin la columna TS_TIMESTAMP. Haremos las inserciones y después añadiremos la columna TS_TIMESTAMP con el NOT NULL WITH DEFAULT (esto debe hacer que todas las filas tomen el TIMESTAMP del momento del ALTER TABLE para la nueva columna):

 BTEQ -- Enter your SQL request or BTEQ command:
DROP TABLE CARLOS.PRUEBA01;


 *** Table has been dropped.
 *** Total elapsed time was 2 seconds.


 BTEQ -- Enter your SQL request or BTEQ command:
CREATE MULTISET TABLE CARLOS.PRUEBA01
   ( ID_N INTEGER NOT NULL)
;


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


 BTEQ -- Enter your SQL request or BTEQ command:


INSERT INTO CARLOS.PRUEBA01(ID_N) VALUES ( 1 )
;INSERT INTO CARLOS.PRUEBA01(ID_N) VALUES ( 2 )
;INSERT INTO CARLOS.PRUEBA01(ID_N) VALUES ( 3 )
;


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

 *** Insert completed. One row added.

 *** Insert completed. One row added.

 BTEQ -- Enter your SQL request or BTEQ command:
ALTER TABLE CARLOS.PRUEBA01
ADD TS_TIMESTAMP TIMESTAMP(0) NOT NULL WITH DEFAULT;


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


 BTEQ -- Enter your SQL request or BTEQ command:
SELECT *
  FROM CARLOS.PRUEBA01
;


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

       ID_N         TS_TIMESTAMP
-----------  -------------------
          3  2014-09-18 18:34:03
          2  2014-09-18 18:34:03
          1  2014-09-18 18:34:03

Añadimos una fila más como marcador:

BTEQ -- Enter your SQL request or BTEQ command:
INSERT INTO CARLOS.PRUEBA01(ID_N) VALUES ( 4 );

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

BTEQ -- Enter your SQL request or BTEQ command:

SELECT *
FROM CARLOS.PRUEBA01
;

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

ID_N TS_TIMESTAMP
----------- -------------------
3 2014-09-18 18:34:03
4 2014-09-18 18:34:19
2 2014-09-18 18:34:03
1 2014-09-18 18:34:03

Si hacemos la misma comparación con el mayor estricto para el TIMESTAMP vemos que:

 BTEQ -- Enter your SQL request or BTEQ command:
SELECT *
  FROM CARLOS.PRUEBA01
 WHERE TS_TIMESTAMP > TIMESTAMP '2014-09-18 18:34:03'
;


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

       ID_N         TS_TIMESTAMP
-----------  -------------------
          3  2014-09-18 18:34:03
          4  2014-09-18 18:34:19
          2  2014-09-18 18:34:03
          1  2014-09-18 18:34:03

Y aunque hemos comparado con un mayor estricto, la ‘SELECT’ devuelve valores como si fuera un mayor o igual.

Pero esto sólo ocurre para las filas que fueron afectadas por el ALTER TABLE, ya que la fila que se insertó con la columna TS_TIMESTAMP ya incorporada, se comporta bien en las comparaciones con mayor estricto:

 BTEQ -- Enter your SQL request or BTEQ command:
SELECT *
  FROM CARLOS.PRUEBA01
WHERE TS_TIMESTAMP > TIMESTAMP '2014-09-18 18:34:19';


 *** Query completed. No rows found.
 *** Total elapsed time was 1 second.

Como los buenos prestidigitadores, mostramos la estructura de la tabla para demostrar que no hay trampa ni cartón:

 BTEQ -- Enter your SQL request or BTEQ command:
SHOW TABLE CARLOS.PRUEBA01;


 *** Text of DDL statement returned.
 *** Total elapsed time was 1 second.

-----------------------------------------------------------------------
CREATE MULTISET TABLE CARLOS.PRUEBA01 ,NO FALLBACK ,
     NO BEFORE JOURNAL,
     NO AFTER JOURNAL,
     CHECKSUM = DEFAULT,
     DEFAULT MERGEBLOCKRATIO
     (
      ID_N INTEGER NOT NULL,
      TS_TIMESTAMP TIMESTAMP(0) NOT NULL DEFAULT CURRENT_TIMESTAMP(0))
PRIMARY INDEX ( ID_N );

A mí esto me suena a ‘bug‘… y ocurre en las versiones 13.10 y 14.10 (¡!).

Así que, cuidadito con los ALTER TABLE añadiendo columnas TIMESTAMP WITH DEFAULT, te puedes llevar la misma sorpresa que me llevé yo hoy…

Saludos.

Carlos.


Windows is still Windows…

10 septiembre \10\UTC 2014

Hacía mucho que no…

image

BSoD

Saludos.

Carlos.


Et in arcadia ego…

4 septiembre \04\UTC 2014

Un clásico.

image

Saludos.

Carlos.


400.000

2 septiembre \02\UTC 2014

400.000 visitas.  Quién me lo iba a decir…

400.000 Saludos.

Carlos.


Seguir

Recibe cada nueva publicación en tu buzón de correo electrónico.

Únete a otros 45 seguidores