‘Bug’ en DISTINCT y GROUP BY en Teradata 13.0

Hay una larga historia sobre la diferencia de comportamiento de DISTINCT y GROUP BY en Teradata. Se ha discutido mucho sobre ello en diferentes sitios, incluso en este blog. El caso es que para la versión 13 Teradata ha cambiado las rutinas internas de comportamiento para -por lo visto- hacer que DISTINCT trabaje de forma parecida a GROUP BY ya que, según dicen, el optimizador puede analizar los ‘QUERY PLANS’ mejor.

Un lector de este ‘blog‘ me envió un mensaje de correo en el que me preguntaba por un extraño comportamiento de SELECTs con cláusulas ‘DISTINCT’ en su nuevo y flamante Teradata V13, al que estaba migrando desde un V6.

El caso es que no pude reproducir el caso de ejemplo que me proporcionaba. Bueno, sí pude, pero en Teradata V12 todo iba OK.

Al ver el resultado de su caso de prueba, y tras descartar por su simplicidad posibles errores, le indiqué que podría tratarse de algún tipo de ‘bug’.

Para verlo con mis propios ojos me monté en casa un pequeño laboratorio con un Teradata 13 y procedí a reproducir el caso de prueba que el lector -Carlos, un tocayo- me había proporcionado. Lo reproduje tal cual, para evitar posibles resultados diferentes al que él obtenía:

 BTEQ -- Enter your DBC/SQL request or BTEQ command:
CREATE MULTISET TABLE PRUEBA_FALLO_1 ,
     NO FALLBACK ,
     NO BEFORE JOURNAL,
     NO AFTER JOURNAL,
     CHECKSUM = DEFAULT
     (
        FECHA DATE FORMAT 'DD-MM-YYYY'
     )
     UNIQUE PRIMARY INDEX ( FECHA )
;

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

 BTEQ -- Enter your DBC/SQL request or BTEQ command:
INSERT INTO PRUEBA_FALLO_1 SELECT DATE;

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


 BTEQ -- Enter your DBC/SQL request or BTEQ command:
INSERT INTO PRUEBA_FALLO_1 SELECT DATE-1;

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


 BTEQ -- Enter your DBC/SQL request or BTEQ command:
INSERT INTO PRUEBA_FALLO_1 SELECT DATE+1;

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

 BTEQ -- Enter your DBC/SQL request or BTEQ command:
SELECT * FROM PRUEBA_FALLO_1;

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

     FECHA
----------
23-01-2011
24-01-2011
22-01-2011


 BTEQ -- Enter your DBC/SQL request or BTEQ command:
CREATE MULTISET TABLE PRUEBA_FALLO_2 ,
     NO FALLBACK ,
     NO BEFORE JOURNAL,
     NO AFTER JOURNAL,
     CHECKSUM = DEFAULT
     (
        FECHA DATE FORMAT 'DD-MM-YYYY'
     )
     PRIMARY INDEX ( FECHA )
;

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


 BTEQ -- Enter your DBC/SQL request or BTEQ command:
INSERT INTO PRUEBA_FALLO_2 SELECT DATE;

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


 BTEQ -- Enter your DBC/SQL request or BTEQ command:
INSERT INTO PRUEBA_FALLO_2 SELECT DATE-1;

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


 BTEQ -- Enter your DBC/SQL request or BTEQ command:
INSERT INTO PRUEBA_FALLO_2 SELECT DATE+1;

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


 BTEQ -- Enter your DBC/SQL request or BTEQ command:
SELECT * FROM PRUEBA_FALLO_2;

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

     FECHA
----------
23-01-2011
24-01-2011
22-01-2011

Resumiendo: dos sencillas tablas con una sola columna de fecha con la única diferencia de que la primera está creada con un UPI y la segunda con un NUPI. Ambas se cargan con tres filas de fechas “hoy”, “ayer”, “mañana”.

Y ahora empieza lo bueno:

Ejecutamos un SELECT ‘DISTINCT’ concatenando año y mes y convirtiéndolo en CHAR(7):

 BTEQ -- Enter your DBC/SQL request or BTEQ command:
SELECT DISTINCT
       ((FECHA (FORMAT 'YYYY')) ) || 
       ((FECHA (FORMAT 'MM')) ) (CHAR(07))
  FROM PRUEBA_FALLO_1
;

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

(FECHA||FECHA)
--------------
201101

Perfecto. Todo según lo previsto. Pero ahora hacemos lo mismo incluyendo un suión separador entre el año y el mes:

 BTEQ -- Enter your DBC/SQL request or BTEQ command:
SELECT DISTINCT
       ((FECHA (FORMAT 'YYYY'))) || '-' || 
       ((FECHA (FORMAT 'MM'))) (CHAR(07))
  FROM PRUEBA_FALLO_1
;

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

((FECHA||'-')||FECHA)
---------------------
2011-01
2011-01
2011-01

WTF!! ¿Pero qué es esto? Resulta que el DISTINCT no hace DISTINCT ni por lo más remoto. Lo más gracioso es que si efectuamos la misma SELECT con la tabla generada con NUPI la cosa funciona bien:

 BTEQ -- Enter your DBC/SQL request or BTEQ command:
SELECT DISTINCT
       ((FECHA (FORMAT 'YYYY')) ) || '-' || 
       ((FECHA (FORMAT 'MM')) ) (CHAR(07))
  FROM PRUEBA_FALLO_2
;

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

((FECHA||'-')||FECHA)
---------------------
2011-01

En efecto, el fallo aparece nítido. La tabla PRUEBA_FALLO_1, creada con un UPI, produce resultados incorrectos cuando aplicamos un DISTINCT sobre una concatenación con un guión en medio más un CAST final. Funciona bien en el caso de que no exista tal guión y también funciona bien con guión para la tabla con NUPI.

Pero además, la SELECT con guión sobre la tabla con UPI también falla con un GROUP BY:

 BTEQ -- Enter your DBC/SQL request or BTEQ command:
SELECT ((FECHA (FORMAT 'YYYY')) ) || '-' || 
       ((FECHA (FORMAT 'MM')) ) (CHAR(07))
  FROM PRUEBA_FALLO_1
 GROUP BY 1
;

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

((FECHA||'-')||FECHA)
---------------------
2011-01
2011-01
2011-01

Según me comenta el lector, se ha puesto en contacto con el soporte Teradata y le han confirmado la existencia del ‘bug‘.

Dicho ‘bug‘ aparece en las versiones 13.0.0.0 a 13.0.0.24. en las condiciones siguientes:

1. CAST o TRIM sobre columna de un UNIQUE INDEX (UPI o USI) y
2. Hay un DISTINCT o GROUP BY sobre dicha columna.

Se corrige en versiones sucesivas 13.0.0.25, 13.0.1.1, 13.10.0.1. También se corrige sin efectuar ‘upgrade’ o aplicar parches ejecutando “diagnostic nodistinctopt on for session;”.

Así que ojo a consultas de este tipo si estáis migrando un sistema a -o simplemente desarrollando sobre- las versiones 13.0.0.0 a 13.0.0.24.

Y gracias al lector -Carlos- por su valiosa información.

Saludos.

Carlos.

Responder

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: