none
CONCATENAR FILAS EN COLUMNAS RRS feed

  • Pregunta

  • Hola grupo soy nuevo en esto, agradezco las personas que me puedan colaborar con un problema que tengo, tengo una consulta SQL en la que tengo valores repetidos en las filas como en este caso itemCode,dscription son iguales cambian algunas columnas, quisiera saber como puedo concatenar las filas para que queden en una sola columna por itemCode  por ejemplo en esta consula solo me deberian aparecer 3 registros y no 6, necesitaria concatenar el causal  ej: DIFERENCIA EN PRECIO,DIFERENCIA EN ICO en una sola columna por item, muchas gracias por colaborarme y por compartir su conocimiento

    pongo el link para poder ver la imagen ya que por nuevo no me dejo ponerla :( gracias

    https://drive.google.com/file/d/1b01lfrDUAgwcQqzD9jsWMReCJBz1GAis/view?usp=sharing

    martes, 23 de octubre de 2018 3:13

Respuestas

  • Hola Diyer Alexander M:

    Por lo que he entendido de tu consulta, en realidad necesitas mostrar parte de las columnas de una fila adicionadas a su fila siguiente.

    Te voy a poner un ejemplo completo con la creación de la tabla y los valores, tal cual los has puesto en la imágen.

    Si bien la consulta, para ti solo tiene que ser lo que va en el bloque with sobre la tabla que yo he llamado tras, y en la select posterior al with

    CREATE TABLE tras
    (lineNum     INT,
     id          INT,
     itemCode    INT,
     description VARCHAR(100),
     mensaje     VARCHAR(100),
     causal      VARCHAR(100),
     tipoError   INT,
     id_linea    INT,
     quantity    INT
    );
    GO
    INSERT INTO tras
    (lineNum,
     id,
     itemCode,
     description,
     mensaje,
     causal,
     tipoError,
     id_linea,
     quantity
    )
    VALUES
    (1,
     338250,
     101037,
     'VINO XX',
     'EL CODIGO XXX VALOR Y',
     'DIF PRECIOS',
     2,
     1608054,
     24
    ),
    (1,
     338250,
     101037,
     'VINO XX',
     'EL CODIGO XXX VALOR Z',
     'DIF ICO',
     2,
     1608054,
     24
    ),
    (0,
     338250,
     101051,
     'VINO XX',
     'EL CODIGO PPP VALOR A',
     'DIF PRECIOS',
     2,
     1608053,
     24
    ),
    (0,
     338250,
     101051,
     'VINO XX',
     'EL CODIGO PPP VALOR B',
     'DIF ICO',
     2,
     1608053,
     24
    ),
    (1,
     338250,
     105045,
     'TEQUILA XX',
     'EL CODIGO XXX VALOR Y',
     'DIF PRECIOS',
     2,
     1608055,
     12
    ),
    (1,
     338250,
     105045,
     'TEQUILA XX',
     'EL CODIGO XXX VALOR Y',
     'DIF ICO',
     2,
     1608055,
     12
    );
    GO
    WITH cte
         AS (
         SELECT ROW_NUMBER() OVER(PARTITION BY ITEMCODE ORDER BY ITEMCODE) AS FILA,
                LINENUM,
                ID,
                ITEMCODE,
                description,
                MENSAJE,
                CAUSAL,
                tipoError,
                ID_LINEA,
                quantity
         FROM TRAS)
         SELECT C.lineNum,
                C.ID,
                C.itemCode,
                C.description,
                C.mensaje,
                C.causal + D.causal AS CAUSAL
         FROM cte C
              LEFT JOIN CTE D ON C.ITEMCODE = D.ITEMCODE
                                 AND C.FILA = D.FILA + 1
         WHERE(NOT D.itemCode IS NULL);
                  

    En el select último observarás que dispones de los dos conjuntos y que puedes concatenar varías columnas.

    Esta creo, que puede ser una vía para solucionar lo que comentas.

    martes, 23 de octubre de 2018 6:04
  • Pudieras decirnos cual version de SQL Server usas?

    Desde la version 2012 contamos con las funciones de off-set LAG/LEAD, las cuales pueden ser muy util en este caso.

    WITH R AS (
    SELECT
        lineNum,
        id,
        itemCode,
        description,
        mensaje,
        causal,
        LAG(causal) OVER(PARTITION BY itemcode ORDER BY (SELECT NULL)) AS prv_causal,
        tipoError,
        id_linea,
        quantity
    FROM
        tras
    )
    SELECT
    	*
    FROM
    	R
    WHERE
    	prv_causal IS NOT NULL
    ;
    GO

    Ambas soluciones, la sugerida por Javi y esta, tienen un problema comun y es que para hablar de previo y siguiente se necesita un orden dado y en estas dos soluciones no existe orden (Sol 1 - Itemcode se repite / Sol 2 - SELECT NULL significa no orden).  Esta falta de orden hace la solucion no determinista (mismo resultado cuando usamos misma data) y corres el riesgo de que cada vez que corras el query el resultado sea distinto.

    Ejemplo:

    ItemCode Quantity Causal
    101037    10          DIF PRECIOS
    101037    10          DIF ICO

    Cual es el orden de estas filas?

    Basado en la clausula ORDER BY usada en ambas soluciones, cualquier fila puede ser la primera o la segunda.

    Supongamos que adicionamos una columna con propiedad identity para simular el orden:

    ItemCode Quantity Causal            sk
    101037    10          DIF PRECIOS  1
    101037    10          DIF ICO          2

    Ahora podemos usar OVER(PARTITION BY ItemCode ORDER BY sk) y el resultado siempre sera el mismo cuando se ejecuta con la misma data.

    DECLARE @tras table
    (lineNum     INT,
     id          INT,
     itemCode    INT,
     description VARCHAR(100),
     mensaje     VARCHAR(100),
     causal      VARCHAR(100),
     tipoError   INT,
     id_linea    INT,
     quantity    int,
     sk int NOT NULL IDENTITY UNIQUE
    );
    
    INSERT INTO @tras
    (lineNum,
     id,
     itemCode,
     description,
     mensaje,
     causal,
     tipoError,
     id_linea,
     quantity
    )
    VALUES
    (1,
     338250,
     101037,
     'VINO XX',
     'EL CODIGO XXX VALOR Y',
     'DIF PRECIOS',
     2,
     1608054,
     24
    ),
    (1,
     338250,
     101037,
     'VINO XX',
     'EL CODIGO XXX VALOR Z',
     'DIF ICO',
     2,
     1608054,
     24
    ),
    (0,
     338250,
     101051,
     'VINO XX',
     'EL CODIGO PPP VALOR A',
     'DIF PRECIOS',
     2,
     1608053,
     24
    ),
    (0,
     338250,
     101051,
     'VINO XX',
     'EL CODIGO PPP VALOR B',
     'DIF ICO',
     2,
     1608053,
     24
    ),
    (1,
     338250,
     105045,
     'TEQUILA XX',
     'EL CODIGO XXX VALOR Y',
     'DIF PRECIOS',
     2,
     1608055,
     12
    ),
    (1,
     338250,
     105045,
     'TEQUILA XX',
     'EL CODIGO XXX VALOR Y',
     'DIF ICO',
     2,
     1608055,
     12
    );
    
    WITH R AS (
    SELECT
        lineNum,
        id,
        itemCode,
        description,
        mensaje,
        causal,
        LAG(causal) OVER(PARTITION BY itemcode ORDER BY sk) AS prv_causal,
        tipoError,
        id_linea,
        quantity,
    	sk
    FROM
    	@tras
    )
    SELECT
    	*
    FROM
    	R
    WHERE
    	prv_causal IS NOT NULL
    ;

    Gracias a Javi por postear data de ejemplo.


    AMB

    Some guidelines for posting questions...

    AYÚDANOS A AYUDARTE, guía básica de consejos para formular preguntas


    martes, 23 de octubre de 2018 14:22

Todas las respuestas

  • Hola Diyer Alexander M:

    Por lo que he entendido de tu consulta, en realidad necesitas mostrar parte de las columnas de una fila adicionadas a su fila siguiente.

    Te voy a poner un ejemplo completo con la creación de la tabla y los valores, tal cual los has puesto en la imágen.

    Si bien la consulta, para ti solo tiene que ser lo que va en el bloque with sobre la tabla que yo he llamado tras, y en la select posterior al with

    CREATE TABLE tras
    (lineNum     INT,
     id          INT,
     itemCode    INT,
     description VARCHAR(100),
     mensaje     VARCHAR(100),
     causal      VARCHAR(100),
     tipoError   INT,
     id_linea    INT,
     quantity    INT
    );
    GO
    INSERT INTO tras
    (lineNum,
     id,
     itemCode,
     description,
     mensaje,
     causal,
     tipoError,
     id_linea,
     quantity
    )
    VALUES
    (1,
     338250,
     101037,
     'VINO XX',
     'EL CODIGO XXX VALOR Y',
     'DIF PRECIOS',
     2,
     1608054,
     24
    ),
    (1,
     338250,
     101037,
     'VINO XX',
     'EL CODIGO XXX VALOR Z',
     'DIF ICO',
     2,
     1608054,
     24
    ),
    (0,
     338250,
     101051,
     'VINO XX',
     'EL CODIGO PPP VALOR A',
     'DIF PRECIOS',
     2,
     1608053,
     24
    ),
    (0,
     338250,
     101051,
     'VINO XX',
     'EL CODIGO PPP VALOR B',
     'DIF ICO',
     2,
     1608053,
     24
    ),
    (1,
     338250,
     105045,
     'TEQUILA XX',
     'EL CODIGO XXX VALOR Y',
     'DIF PRECIOS',
     2,
     1608055,
     12
    ),
    (1,
     338250,
     105045,
     'TEQUILA XX',
     'EL CODIGO XXX VALOR Y',
     'DIF ICO',
     2,
     1608055,
     12
    );
    GO
    WITH cte
         AS (
         SELECT ROW_NUMBER() OVER(PARTITION BY ITEMCODE ORDER BY ITEMCODE) AS FILA,
                LINENUM,
                ID,
                ITEMCODE,
                description,
                MENSAJE,
                CAUSAL,
                tipoError,
                ID_LINEA,
                quantity
         FROM TRAS)
         SELECT C.lineNum,
                C.ID,
                C.itemCode,
                C.description,
                C.mensaje,
                C.causal + D.causal AS CAUSAL
         FROM cte C
              LEFT JOIN CTE D ON C.ITEMCODE = D.ITEMCODE
                                 AND C.FILA = D.FILA + 1
         WHERE(NOT D.itemCode IS NULL);
                  

    En el select último observarás que dispones de los dos conjuntos y que puedes concatenar varías columnas.

    Esta creo, que puede ser una vía para solucionar lo que comentas.

    martes, 23 de octubre de 2018 6:04
  • Hola Javi, Muchas gracias por tu respuesta, lo probe y me funciona y es lo que necesito, tengo una pregunta en la parte del left join me confundi un poco ya que haces referencia a una tabla con el alias D para hacer el cruze entre ellas, me podrias explicar esa parte gracias
    martes, 23 de octubre de 2018 13:49
  • Pudieras decirnos cual version de SQL Server usas?

    Desde la version 2012 contamos con las funciones de off-set LAG/LEAD, las cuales pueden ser muy util en este caso.

    WITH R AS (
    SELECT
        lineNum,
        id,
        itemCode,
        description,
        mensaje,
        causal,
        LAG(causal) OVER(PARTITION BY itemcode ORDER BY (SELECT NULL)) AS prv_causal,
        tipoError,
        id_linea,
        quantity
    FROM
        tras
    )
    SELECT
    	*
    FROM
    	R
    WHERE
    	prv_causal IS NOT NULL
    ;
    GO

    Ambas soluciones, la sugerida por Javi y esta, tienen un problema comun y es que para hablar de previo y siguiente se necesita un orden dado y en estas dos soluciones no existe orden (Sol 1 - Itemcode se repite / Sol 2 - SELECT NULL significa no orden).  Esta falta de orden hace la solucion no determinista (mismo resultado cuando usamos misma data) y corres el riesgo de que cada vez que corras el query el resultado sea distinto.

    Ejemplo:

    ItemCode Quantity Causal
    101037    10          DIF PRECIOS
    101037    10          DIF ICO

    Cual es el orden de estas filas?

    Basado en la clausula ORDER BY usada en ambas soluciones, cualquier fila puede ser la primera o la segunda.

    Supongamos que adicionamos una columna con propiedad identity para simular el orden:

    ItemCode Quantity Causal            sk
    101037    10          DIF PRECIOS  1
    101037    10          DIF ICO          2

    Ahora podemos usar OVER(PARTITION BY ItemCode ORDER BY sk) y el resultado siempre sera el mismo cuando se ejecuta con la misma data.

    DECLARE @tras table
    (lineNum     INT,
     id          INT,
     itemCode    INT,
     description VARCHAR(100),
     mensaje     VARCHAR(100),
     causal      VARCHAR(100),
     tipoError   INT,
     id_linea    INT,
     quantity    int,
     sk int NOT NULL IDENTITY UNIQUE
    );
    
    INSERT INTO @tras
    (lineNum,
     id,
     itemCode,
     description,
     mensaje,
     causal,
     tipoError,
     id_linea,
     quantity
    )
    VALUES
    (1,
     338250,
     101037,
     'VINO XX',
     'EL CODIGO XXX VALOR Y',
     'DIF PRECIOS',
     2,
     1608054,
     24
    ),
    (1,
     338250,
     101037,
     'VINO XX',
     'EL CODIGO XXX VALOR Z',
     'DIF ICO',
     2,
     1608054,
     24
    ),
    (0,
     338250,
     101051,
     'VINO XX',
     'EL CODIGO PPP VALOR A',
     'DIF PRECIOS',
     2,
     1608053,
     24
    ),
    (0,
     338250,
     101051,
     'VINO XX',
     'EL CODIGO PPP VALOR B',
     'DIF ICO',
     2,
     1608053,
     24
    ),
    (1,
     338250,
     105045,
     'TEQUILA XX',
     'EL CODIGO XXX VALOR Y',
     'DIF PRECIOS',
     2,
     1608055,
     12
    ),
    (1,
     338250,
     105045,
     'TEQUILA XX',
     'EL CODIGO XXX VALOR Y',
     'DIF ICO',
     2,
     1608055,
     12
    );
    
    WITH R AS (
    SELECT
        lineNum,
        id,
        itemCode,
        description,
        mensaje,
        causal,
        LAG(causal) OVER(PARTITION BY itemcode ORDER BY sk) AS prv_causal,
        tipoError,
        id_linea,
        quantity,
    	sk
    FROM
    	@tras
    )
    SELECT
    	*
    FROM
    	R
    WHERE
    	prv_causal IS NOT NULL
    ;

    Gracias a Javi por postear data de ejemplo.


    AMB

    Some guidelines for posting questions...

    AYÚDANOS A AYUDARTE, guía básica de consejos para formular preguntas


    martes, 23 de octubre de 2018 14:22
  • Hola Diyer Alexander:

    Eso se llama self join, o lo que es lo mismo relacionar una tabla consigo misma. Para indicarle al motor sobre cual de los dos conjuntos accede, tienes que trabajar con alias.

    Si pones select c.*, d.* from cte c left join cte d... y le quitas el where. observarás, realmente lo que esta haciendo.

    Espero te ayude.

    martes, 23 de octubre de 2018 14:23
  • Muchas gracias por tu respuesta tambien me funciono, y si efectivamente estoy utilizando la version 2012, gracias
    martes, 23 de octubre de 2018 14:57
  • vale Javi, muchas gracias me quedo mucho mas claro! muchas gracias
    martes, 23 de octubre de 2018 14:58
  • De nada. Diyer Alexander M

    Gracias a ti Hunchback por aportar siempre algo más.

    martes, 23 de octubre de 2018 19:16