Raíz n-ésima en Teradata.

9 marzo \09\UTC 2016

En los cálculos habituales a los que estamos acostumbrados normalmente sólo se necesitan raíces cuadradas (y eso si alguna vez se necesitan). Para ello existe la función SQRT (square root) que es simple y sencilla.

SELECT SQRT(16);


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

              Sqrt(16)
----------------------
 4.00000000000000E 000

¿Pero qué pasa si queremos una raíz cúbica, cuarta o cualquier otra raíz n-ésima? Pues que entonces tenemos que buscar otro método.
Lo normal es utilizar una exponenciación inversa: SQRT(16)=16**(1/2)

Así que:

BTEQ -- Enter your SQL request or BTEQ command:
SELECT 243**(1/5);


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

          (243**(1/5))
----------------------
 1.00000000000000E 000

¿1? ¿Cómo es posible? Pues claro, porque 1/5 trunca a 0, y n**0 = 1.

Entonces hay que hacer un CAST para evitarlo:

SELECT 243**(1(FLOAT)/5);
                       $
 *** Failure 3706 Syntax error: expected something between ')' and '/'.
                Statement# 1, Info =24
 *** Total elapsed time was 1 second.

Con un CAST ‘TERADATA’ casca. Hay que hacer un CAST ‘ANSI’.

 BTEQ -- Enter your SQL request or BTEQ command:
SELECT 243**CAST(1 AS FLOAT)/5;


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

          ((243**1)/5)
----------------------
 4.86000000000000E 001

Esto tampoco es lo que esperábamos. Pero aquí el problema está en la precedencia de operadores: se ejecuta primero 243**1 y luego se divide por 5.
Es decir: (243**1)/5 = 48.6

Así que lo mejor será utilizar los paréntesis con criterio:

 BTEQ -- Enter your SQL request or BTEQ command:
SELECT 243**(CAST(1 AS FLOAT)/5);


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

          (243**(1/5))
----------------------
 3.00000000000000E 000

Y esto sí es lo que esperábamos.

Por último, aunque podemos utilizar la potenciación con números negativos:

SELECT (-2)**(3);


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

               (-2**3)
----------------------
-8.00000000000000E 000

 BTEQ -- Enter your SQL request or BTEQ command:
SELECT (-8)**(3);


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

               (-8**3)
----------------------
-5.12000000000000E 002

No podemos hacer lo mismo con exponentes menores que 1:

 BTEQ -- Enter your SQL request or BTEQ command:
SELECT (-8)**(CAST(1 AS FLOAT)/3);

 *** Failure 2622 Bad argument for ** operator.
                Statement# 1, Info =0
 *** Total elapsed time was 1 second.

También hay que tener en cuenta que estamos trabajando con FLOATs de doble precisión y sus posibles errores inherentes.

Saludos.

Carlos.


Cadenas a filas con STRTOK_SPLIT_TO_TABLE()

18 enero \18\UTC 2016

La conversión de columnas de cadenas de datos separados por un ‘token’ en filas se ha tratado ya en este blog. Entonces se hizo utilizando recursividad (WITH RECURSIVE…), en esta ocasión vamos a ver cómo se puede hacer con la nueva función STRTOK_SPLIT_TO_TABLE() -disponible en Teradata desde la versión 14.0- y algunas particularidades que tiene su uso.

La función hace lo que dice su nombre: convierte en tablas cadenas delimitadas por ‘tokens’. Pero hace falta saber un poco más de ella: lo principal es que necesita un valor ‘key’ para referenciar las filas de la tabla resultante de la cadena de ‘tokens’ a partir de las filas de la tabla original. Esta clave funciona como una especie de PK/FK.

Las columnas de cadenas separadas por ‘tokens’ pueden ser tanto caracteres “normales” (CHAR, VARCHAR) como CLOB. La cadena ‘token’ (delimitador) puede ser de hasta 64 caracteres (el tipo es VARCHAR(64)), aunque normalmente será un único caracter (de hecho, su “DEFAULT” es el espacio simple (‘ ‘)).

Bueno, pues con todo esto vamos a crearnos la consabida tabla de ejemplos:

 BTEQ -- Enter your SQL request or BTEQ command:
CREATE MULTISET TABLE CARLOS.PRUEBA_08 ,
     NO FALLBACK ,
     NO BEFORE JOURNAL,
     NO AFTER JOURNAL,
     CHECKSUM = DEFAULT,
     DEFAULT MERGEBLOCKRATIO
     (
      ID_N INTEGER NOT NULL,
      C_TXT CHAR(1) NOT NULL,
      V_TXT VARCHAR(25) 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.PRUEBA_08 VALUES (1,'A','001');


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


 BTEQ -- Enter your SQL request or BTEQ command:
INSERT INTO CARLOS.PRUEBA_08 VALUES (2,'B','002,003');


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


 BTEQ -- Enter your SQL request or BTEQ command:
INSERT INTO CARLOS.PRUEBA_08 VALUES (4,'D','004,005,006');


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


 BTEQ -- Enter your SQL request or BTEQ command:
SELECT * FROM CARLOS.PRUEBA_08
ORDER BY 1,2;


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

       ID_N  C_TXT  V_TXT
-----------  -----  -------------------------
          1  A      001
          2  B      002,003
          4  D      004,005,006

Basándonos en la definición y ejemplos de la documentación Teradata, podemos hacer:

 BTEQ -- Enter your SQL request or BTEQ command:
SELECT d.*
FROM TABLE (TD_SYSFNLIB.STRTOK_SPLIT_TO_TABLE(CARLOS.PRUEBA_08.ID_N, 
                                              CARLOS.PRUEBA_08.V_TXT, 
                                              ',')
            RETURNS (outkey integer, 
                     tokennum integer, 
                     token varchar(20) character set unicode) 
           ) as d 
ORDER BY 1,2;


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

     outkey     tokennum  token
-----------  -----------  --------------------
          1            1  001
          2            1  002
          2            2  003
          4            1  004
          4            2  005
          4            3  006

¿Qué hemos tenido en cuenta? Pues que hemos elegido la columna ID_N como nuestra clave y la coma ‘,’ como nuestro separador. Fácil.

Pero supongamos que quermos ir un poco más allá: que queremos conservar en lo posible la estructura de la tabla original, con sus tres columnas (aunque podrían ser más). Entonces tenemos que recurrir a una versión un poco más complicada. Lo primero es que la clave elegida (inkey/outkey) puede ser VARCHAR y, aunque la documentación dice que puede ser de hasta 10 caracteres (VARCHAR(10)), lo cierto es que admite hasta VARCHAR(32). Otra cosa muy importante a tener en cuenta es que esta clave VARCHAR debe ser CHARACTER SET UNICODE (al igual que el valor de cada parte de la cadena devuelta).

Sabiendo esto, podemos concatenar las columnas que necesitemos (hasta un límite de VARCHAR(32)):

          
SELECT d.*
FROM TABLE (TD_SYSFNLIB.STRTOK_SPLIT_TO_TABLE(
               CAST(CARLOS.PRUEBA_08.ID_N AS CHAR(1)) || '|' ||
                  CARLOS.PRUEBA_08.C_TXT, 
               CARLOS.PRUEBA_08.V_TXT, 
               ',')
            RETURNS (
               outkey VARCHAR(32) character set unicode, 
               tokennum integer, 
               token varchar(20) character set unicode)
           ) d 
ORDER BY 1,2;


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

outkey         tokennum  token
----------  -----------  --------------------
1|A                   1  001
2|B                   1  002
2|B                   2  003
4|D                   1  004
4|D                   2  005
4|D                   3  006

OK. Entonces podemos volver a descomponer la clave en sus columnas originales utilizando la función STRTOK(), que es prima hermana de nuestra STRTOK_SPLIT_TO_TABLE(), y utilizar simples CAST para que el resultado sea lo más parecido a nuestra tabla original:

 BTEQ -- Enter your SQL request or BTEQ command:
SELECT CAST(TD_SYSFNLIB.STRTOK(d.OUTKEY,'|',1) AS INTEGER) ID_N,
       CAST(TD_SYSFNLIB.STRTOK(d.OUTKEY,'|',2) AS CHAR(1)) C_TXT,
       CAST(d.TOKEN AS VARCHAR(20)) V_TXT_PART
FROM TABLE (TD_SYSFNLIB.STRTOK_SPLIT_TO_TABLE(
               CAST(CARLOS.PRUEBA_08.ID_N AS CHAR(1)) || '|' ||
                  CARLOS.PRUEBA_08.C_TXT, 
               CARLOS.PRUEBA_08.V_TXT, 
               ',')
            RETURNS (OUTKEY VARCHAR(32) CHARACTER SET UNICODE, 
                     TOKENNUM INTEGER, 
                     TOKEN VARCHAR(20) CHARACTER SET UNICODE)
           ) d 
ORDER BY 1,2,3;


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

       ID_N  C_TXT  V_TXT_PART
-----------  -----  -------------------------
          1  A      001
          2  B      002
          2  B      003
          4  D      004
          4  D      005
          4  D      006

Saludos.

Carlos.


17.000.000.000 filas

16 diciembre \16\UTC 2015

O sea, diecisiete mil millones de filas, es lo que hemos estado moviendo hoy en la base de datos Teradata.

Saludos.

Carlos.


“Logging Online Archive Off”

4 noviembre \04\UTC 2015

Puede que te haya pasado. Miras la tabla de eventos de backup (Dbc.RCEvent) o una de sus vistas (Dbc.EventsV…) y te encuentras un montón de filas con EventType “Logging Online Archive Off”. Y no sabes por qué están ahí y por qué hay tantas.

Dbc.EventsV

Pero vamos por partes: ¿Qué es eso del “Online Archive”? Pues ni más ni menos que un mecanismo para poder hacer un backup (“dump” en términos de arcmain) de tablas que estén siendo actualizadas (INSERT, UPDATE o DELETE). Dicho mecanismo es simple: cuando se inicia el “Logging Online Archive” para una tabla (también se pueden ejecutar para bases de datos) se crea una subtabla de log donde se guardan las filas tal y como estaban en el momento de activar el “logging” (“before image rows”) y también las modificaciones a la tabla en forma de “redo records”. Así se archivará una imagen estable de la tabla más los “redo records”. En caso de recuperar la tabla desde el backup, se recuperará la imagen estable de la tabla y se aplicarán los “redo records” correspondientes a las actualizaciones que ocurrieron durante el backup (“roll forward”).

La activación del “Logging Online Archive” se hace mediante el comando LOGGING ONLINE ARCHIVE ON y se detiene mediante LOGGING ONLINE ARCHIVE OFF, que además borra las subtablas de log existentes.

Y pensarás: ‘todo esto es muy interesante, pero yo no he activado el “Logging Online Archive” y, por supuesto, tampoco lo he desactivado’. Cierto. Entonces ¿de dónde salen los eventos de RCEvent?

Pues los eventos vienen, ni más ni menos, de los comandos DROP TABLE que se hayan ejecutado. Cuando se envía un comando DROP TABLE Teradata ejecuta un “Logging Online Archive Off” para asegurarse de que si existen subtablas de log sean borradas junto con la tabla principal. Además, “Logging Online Archive Off” no devuelve ningún error si se ejecuta sin que se hubiera ejecutado antes un “Logging Online Archive On”, por lo que es una ejecución ‘por si acaso’.

También aparecerán este tipo de eventos provocados por las ejecuciones de utilidades (fastload, multiload…), ya que cuando éstas terminan (sin error) se efectúan DROPs de las tablas de error, trabajo y log.

Saludos.

Carlos.


Lo que nunca debería ocurrir…

29 octubre \29\UTC 2015

…y menos en un entorno de producción:

blocked

Una buena estrategia y configuración de backups es fundamental para no impedir o entorpecer el trabajo de los usuarios y la ejecución de procesos durante las horas de explotación.

Saludos.

Carlos.


Teradata FORMAT: comportamiento caprichoso (cuanto menos)

15 julio \15\UTC 2015

La cláusula FORMAT sirve para fijar el formato con el que se mostrará una columna, pero también influye en el formato que se aceptará en los INSERTs con CASTs implícitos.

Vamos a ver un ejemplo con formatos para DATE y TIME:

SHOW TABLE CARLOS.PRUEBA12;

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

CREATE MULTISET TABLE CARLOS.PRUEBA12 ,NO FALLBACK ,
     NO BEFORE JOURNAL,
     NO AFTER JOURNAL,
     CHECKSUM = DEFAULT,
     DEFAULT MERGEBLOCKRATIO
     (
      ID_N INTEGER NOT NULL,
      D_FECHA DATE FORMAT 'yyyymmdd',
      H_HORA TIME(0) FORMAT 'hhmiss')
PRIMARY INDEX ( ID_N );


 BTEQ -- Enter your SQL request or BTEQ command:
INSERT INTO CARLOS.PRUEBA12 VALUES (1, '20150715',NULL);


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


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


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

El formato definido para la columnna hace incluso que se rechacen cadenas en formato ANSI / ISO-8601:

 BTEQ -- Enter your SQL request or BTEQ command:
INSERT INTO CARLOS.PRUEBA12 VALUES (3, NULL, '18:28:16');

 *** Failure 6758 Invalid time.
                Statement# 1, Info =0
 *** Total elapsed time was 1 second.

 
 BTEQ -- Enter your SQL request or BTEQ command:
INSERT INTO CARLOS.PRUEBA12 VALUES (6, '2015-07-15', NULL);

 *** Failure 3535 A character string failed conversion to a numeric value.
                Statement# 1, Info =0
 *** Total elapsed time was 1 second.
 
 
 BTEQ -- Enter your SQL request or BTEQ command:
SELECT ID_N, D_FECHA, H_HORA
FROM CARLOS.PRUEBA12;


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

       ID_N   D_FECHA  H_HORA
-----------  --------  ------
          1  20150715  (null)
          2    (null)  182716

Así vemos que hemos fallado miserablemente al insertar fechas y horas en formato diferente al establecido con el FORMAT en la creación de la tabla.

Pero ahora es cuando las cosas se lían un poco:

Creamos el fichero C:\Basura\PRUEBAFORMAT.txt como:

3;20150715;183125
4;20150715;183145

E intentamos una carga con un .IMPORT FILE (nótese que las filas en el fichero respetan los formatos definidos):

 BTEQ -- Enter your SQL request or BTEQ command:
.IMPORT VARTEXT ';' FILE C:\Basura\PRUEBAFORMAT.txt;
 BTEQ -- Enter your SQL request or BTEQ command:
.REPEAT *
 BTEQ -- Enter your SQL request or BTEQ command:
USING
  id_n    (VARCHAR(1)),
  d_fecha (VARCHAR(8)),
  h_hora  (VARCHAR(6))
INSERT INTO CARLOS.PRUEBA12 (ID_N, D_FECHA, H_HORA) 
VALUES (:id_n, :d_fecha, :h_hora);
 *** Starting Row 0 at Wed Jul 15 18:37:37 2015

 *** Failure 6758 Invalid time.
                Statement# 1, Info =0
 *** Total elapsed time was 1 second.


 *** Failure 6758 Invalid time.
                Statement# 1, Info =0
 *** Total elapsed time was 1 second.


 *** Warning: Out of data.
 *** Finished at input row 2 at Wed Jul 15 18:37:37 2015
 *** Total number of statements: 2,  Accepted : 0,  Rejected : 2

 *** Total elapsed time was 1 second.

 *** Total requests sent to the DBC = 2
 *** Successful requests per second =  2.000

¡Fallo!

Probamos con la carga del mismo fichero, pero como de longitud fija, por si acaso:

 BTEQ -- Enter your SQL request or BTEQ command:
.IMPORT FILE C:\Basura\PRUEBAFORMAT.txt;
 *** Warning: No IMPORT mode was given, assuming field mode.
 BTEQ -- Enter your SQL request or BTEQ command:
.REPEAT *
 BTEQ -- Enter your SQL request or BTEQ command:
USING
  id_n    (CHAR(1)),
  filler1 (CHAR(1)),
  d_fecha (CHAR(8)),
  filler2 (CHAR(1)),
  h_hora  (CHAR(6))
INSERT INTO CARLOS.PRUEBA12 (ID_N, D_FECHA, H_HORA) 
VALUES (:id_n, :d_fecha, :h_hora);
 *** Starting Row 0 at Wed Jul 15 18:45:12 2015

 *** Failure 6758 Invalid time.
                Statement# 1, Info =0
 *** Total elapsed time was 1 second.


 *** Failure 6758 Invalid time.
                Statement# 1, Info =0
 *** Total elapsed time was 1 second.

 *** Warning: Out of data.
 *** Finished at input row 2 at Wed Jul 15 18:45:12 2015
 *** Total number of statements: 2,  Accepted : 0,  Rejected : 2

 *** Total elapsed time was 1 second.

 *** Total requests sent to the DBC = 2
 *** Successful requests per second =  2.000

¡El mismo fallo!

Pero el formato sigue funcionando para cadenas insertadas ‘a mano’ en sentencias:

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


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


 BTEQ -- Enter your SQL request or BTEQ command:
SELECT ID_N, D_FECHA, H_HORA
FROM CARLOS.PRUEBA12;


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

       ID_N   D_FECHA  H_HORA
-----------  --------  ------
          1  20150715  (null)
          2    (null)  182716
          3  20150715  184415

Y para terminarlo de liar, modificamos el fichero C:\Basura\PRUEBAFORMAT.txt:

4;20150715;
5;20150715;

Es decir, dejamos las fechas según el FORMAT definido, pero hacemos NULL las horas.

Y hete aquí que:

 BTEQ -- Enter your SQL request or BTEQ command:
.IMPORT VARTEXT ';' FILE C:\Basura\PRUEBAFORMAT.txt;
 BTEQ -- Enter your SQL request or BTEQ command:
.REPEAT *
 BTEQ -- Enter your SQL request or BTEQ command:
USING
  id_n    (VARCHAR(1)),
  d_fecha (VARCHAR(8)),
  h_hora  (VARCHAR(6))
INSERT INTO CARLOS.PRUEBA12 (ID_N, D_FECHA, H_HORA) VALUES (:id_n, :d_fecha, :h_hora);
 *** Starting Row 0 at Wed Jul 15 18:48:15 2015


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


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

 *** Warning: Out of data.
 *** Finished at input row 2 at Wed Jul 15 18:48:15 2015
 *** Total number of statements: 2,  Accepted : 2,  Rejected : 0

 *** Total elapsed time was 1 second.

 *** Total requests sent to the DBC = 2
 *** Successful requests per second =  2.000

 BTEQ -- Enter your SQL request or BTEQ command:
SELECT ID_N, D_FECHA, H_HORA
FROM CARLOS.PRUEBA12;


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

       ID_N   D_FECHA  H_HORA
-----------  --------  ------
          1  20150715  (null)
          2    (null)  182716
          3  20150715  184415
          4  20150715  (null)
          5  20150715  (null)

¡Y se insertan!

Resumiendo: Los formatos para fechas y horas definen la validez en las inserciones con CASTs implícitos hechas en sentencias. Pero si utilizamos un fichero con IMPORT FILE sólo funcionan en el caso de las fechas (DATE), y fallan en el caso de las horas (TIME).

Ya lo sé, ya lo sé: a mí tampoco me gusta.

Saludos.

Carlos.


Convertir filas a cadenas en Teradata (II)

25 mayo \25\UTC 2015

El viejo problema de convertir filas a cadenas de caracteres es un tema recurrente y ha sido tratado aquí. Normalmente hay que recurrir a algún tipo de recursividad (incluso en Oracle) y suele resultar un tanto engorroso.

Pero resulta que hay otro método. Aunque es un tanto extraño, ya que necesita utilizar Teradata XML Services, y más específicamente la función XMLAGG.

Esta función agrega múltiples filas para construir un valor XML (devuelve un XMLTYPE). Pero lo interesante es que podemos utilizarla para algo que no tiene que ver con XML: convertir filas en cadenas.

Veamos cómo:

 BTEQ -- Enter your SQL request or BTEQ command:
SELECT * FROM PRUEBA08;


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

       ID_N  C_TXT
-----------  -------------------------
          3  TRES
          1  UNO
          4  CUATRO
          2  DOS


 BTEQ -- Enter your SQL request or BTEQ command:
SELECT XMLAGG(C_TXT ORDER BY ID_N) FROM CARLOS.PRUEBA08;


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

XMLAGG(C_TXT ORDER BY ID_N ASC RETURNING SEQUENCE)
--------------------------------------------------
UNO DOS TRES CUATRO

¿Así de fácil? Casi. Hay que tener en cuenta que XMLAGG se comporta como una función de agregación (sí, como SUM(), COUNT(), MAX()…):

 BTEQ -- Enter your SQL request or BTEQ command:
SELECT ID_N,XMLAGG(C_TXT ORDER BY ID_N) FROM CARLOS.PRUEBA08;

 *** Failure 3504 Selected non-aggregate values must be part of the associated group.
                Statement# 1, Info =0
 *** Total elapsed time was 1 second.

Exacto:

 BTEQ -- Enter your SQL request or BTEQ command:
SELECT ID_N,XMLAGG(C_TXT ORDER BY ID_N) 
FROM CARLOS.PRUEBA08 GROUP BY 1;


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

       ID_N XMLAGG(C_TXT ORDER BY ID_N ASC RETURNING SEQUENCE)
----------- --------------------------------------------------------------------------------
          3 TRES
          1 UNO
          4 CUATRO
          2 DOS

Teniendo eso claro, lo tenemos casi:

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


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


 BTEQ -- Enter your SQL request or BTEQ command:


INSERT INTO CARLOS.PRUEBA08 VALUES (1,'TRES');


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


 BTEQ -- Enter your SQL request or BTEQ command:


INSERT INTO CARLOS.PRUEBA08 VALUES (2,'TRES');


 *** Insert completed. One row added.
 *** Total elapsed time was 1 second.
 
 BTEQ -- Enter your SQL request or BTEQ command:
SELECT * FROM CARLOS.PRUEBA08 ORDER BY 1;


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

       ID_N  C_TXT
-----------  -------------------------
          1  UNO
          1  DOS
          1  TRES
          2  DOS
          2  TRES
          3  TRES
          4  CUATRO

Entonces:

  BTEQ -- Enter your SQL request or BTEQ command:
SELECT ID_N, XMLAGG(C_TXT) 
FROM CARLOS.PRUEBA08 GROUP BY 1 ORDER BY 1;


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

       ID_N XMLAGG(C_TXT RETURNING SEQUENCE)
----------- ----------------------------------------------------------
          1 UNO DOS TRES
          2 DOS TRES
          3 TRES
          4 CUATRO

Sólo queda un asunto menor: XMLAGG devuelve XML:

 BTEQ -- Enter your SQL request or BTEQ command:
SELECT ID_N,
       TYPE(XMLAGG(C_TXT)) 
  FROM CARLOS.PRUEBA08 GROUP BY 1 ORDER BY 1;


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

       ID_N  Type(XMLAGG(C_TXT ORDER BY ID_N ASC RETURNING SEQUENCE))
-----------  --------------------------------------------------------
          1  SYSUDTLIB.XML
          2  SYSUDTLIB.XML
          3  SYSUDTLIB.XML
          4  SYSUDTLIB.XML

Por lo que un ‘cast‘ le pone la guinda al pastel:

 BTEQ -- Enter your SQL request or BTEQ command:
SELECT ID_N, 
       XMLAGG(C_TXT) (VARCHAR(64)) 
  FROM CARLOS.PRUEBA08 GROUP BY 1 ORDER BY 1;


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

       ID_N  XMLAGG(C_TXT RETURNING SEQUENCE)
-----------  ----------------------------------------------------------------
          1  UNO DOS TRES
          2  DOS TRES
          3  TRES
          4  CUATRO

Y si en vez de un espacio queremos un separador específico (‘|’, ‘;’, ‘,’…), es un juego de niños implementarlo.

Saludos.

Carlos.


Seguir

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

Únete a otros 71 seguidores