none
Consultas PIVOT en SQL Server RRS feed

  • Pregunta

  • Buenas devs!
    Tengo una consulta para hacer un procedure:

    Tengo una tabla(TEMP) con estos valores

    ID              Amount  LevelName LevelCode
    1017207    120.00    10       8
    1017207    180.00     70       9
    1017207    150.00     M       10
    1017207    200.00     S       20

    Pero yo necesito mi consulta así así:

    ID  Amount_10  Amount_M  Amount_S
    1017207  120.00  150.00  200.00

    Googleando encontre que se puede hacer por PIVOT, query:
    Select ID, [8], [10], [20] From (Select ID,Amount from TEMP Where LevelName in ('10','M','S') ) as TempT
    PIVOT(Max(Amount) For LevelCode in ([8], [10], [20]) ) As PivotT

    Este query me resulta así:

    ID                 8         10         20
    1017207  120.00  150.00  200.00

    Que es correcto, aunque he aquí mi problema, mi campo LevelName puede variar no necesariamente tomará '10','M','S' como hago yo, para que cuando tome valores estos valores '70','M','S' mi consulta sea algo asi:

    Select ID, [9], [10], [20] From (Select ID,Amount from TEMP Where LevelName in ('70','M','S') ) as TempT
    PIVOT(Max(Amount) For LevelCode in ([9], [10], [20]) ) As PivotT

    ID                 9         10         20
    1017207  180.00  150.00  200.00

    Pense en declarar variables con esos valores, algo asi quedaria el query, pero no va.
    Declare @Level10 varchar(2) = '10'
    Declare @LevelM varchar(2) = 'S'
    Declare @LevelS varchar(2) = 'M'

    Select ID, [@Level10], [@LevelM], [@LevelS] From (Select ID,Amount from TEMP Where LevelName in ('' + @Level10 + '','' + @LevelM +  '','' + @LevelS + '') ) as TempT
    PIVOT(Max(Amount) For LevelCode in ( [@Level10], [@LevelM], [@LevelS]) ) As PivotT

    Como hago que cuando varie los valores que ingresan a mi subconsulta, varie tambien en la consulta y el PIVOT.

    Lo ultimo que hice fue ordenarlos:
    Select ID, [1], [2], [3] From (Select ID,Amount, rownumber over(Order By LevelName) as RowID from TEMP Where LevelName in ('70','M','S') ) as TempT
    PIVOT(Max(Amount) For RowID in ([1], [2], [3]) ) As PivotT

    Asi masomenos sabría que los valores que ingresan serán los mismos que estan en el resultado, aunque no se si sea seguro con el rownumber dentro, Gracias por sus respuestas!

    domingo, 20 de septiembre de 2015 7:02

Respuestas

  • no se si estamos en el mismo asunto creo que sí, puedes hacerlo dinámico de esta forma.

    /*
    create table temp (id int, amount numeric(10,2), levelname char(2),levelcode int)
    insert into temp 
    select 1017207  ,  120.00,    '10',       8
    union all
    select 1017207   , 180.00,     '70' ,      9
     union all
    select 1017207  ,  150.00 ,   ' M'   ,    10
     union all
    select 1017207  ,  200.00  ,  ' S'    ,   20
    */
    
    declare @columns nvarchar(max)
    select @columns=STUFF((
    				select ','+quotename(levelcode) from temp for xml path('')),1,1,'')
    declare @levelname nvarchar(max)
    select @levelname=STUFF((
    				select ','+''''+(levelname)+'''' from temp for xml path('')),1,1,'')
    declare @sql nvarchar(max)
    set @sql=N'Select ID,' +@columns + ' From (Select ID,Amount,levelcode from TEMP Where LevelName in (' +@levelname +' ) ) as TempT
     PIVOT(Max(Amount) For levelcode in ( '+@columns +' )) As PivotT';
      exec sp_executesql @sql,N''


    Comparte lo que sepas, aprende lo que no sepas (FGG)
    portalSQL
    El rincón del DBA

    domingo, 20 de septiembre de 2015 9:34
    Moderador
  • Por que quieres hacer el query de forma dinamica si los valores a pivotear son fijos y conocidos antes de la ejecucion?

    Para realizar la operacion de pivoteo se necesitan los sgtes pasos:

    - Agrupar (GROUP BY)

    - Regar valores (CASE expression)

    - Agregar (MIN / MAX / SUM / etc.)

    Esto se puede hacer con una sentencia SELECT que agrupe por ID, lo mismo que haria el operador PIVOT por detras del telon. La diferencia fundamental entre un metodo y/o el otro es que con el operador PIVOT solo se puede transformar una columna a la vez.

    declare @T table (
    ID int,
    Amount decimal(6, 2),
    LevelName varchar(10),
    LevelCode int
    );
    
    insert into @T 
    	(ID, Amount, LevelName, LevelCode)
    values
    	(1017207, 120.00, '10', 8),
    	(1017207, 180.00, '70', 9),
    	(1017207, 150.00, 'M', 10),
    	(1017207, 200.00, 'S', 20),
    
    	(1017208, 220.00, '10', 8),
    	(1017208, 280.00, '70', 9),
    	(1017208, 250.00, 'M', 10),
    	(1017208, 300.00, 'S', 20);
    
    select
    	ID,
    	MAX(case when LevelName = '10' then Amount end) as Amount_10,
    	MAX(case when LevelName = 'M' then Amount end) as Amount_M,
    	MAX(case when LevelName = 'S' then Amount end) as Amount_S
    from
    	@T
    group by
    	ID;
    go

    Si incistes en hacerlo dinamicamente, cosa que no veo necesaria, aca te dejo como hacerlo de manera simple.

    create table #T (
    ID int,
    Amount decimal(6, 2),
    LevelName varchar(10),
    LevelCode int
    );
    
    insert into #T 
    	(ID, Amount, LevelName, LevelCode)
    values
    	(1017207, 120.00, '10', 8),
    	(1017207, 180.00, '70', 9),
    	(1017207, 150.00, 'M', 10),
    	(1017207, 200.00, 'S', 20),
    
    	(1017208, 220.00, '10', 8),
    	(1017208, 280.00, '70', 9),
    	(1017208, 250.00, 'M', 10),
    	(1017208, 300.00, 'S', 20);
    
    declare 
    	@sql nvarchar(max);
    
    set @sql = N'
    select ID' + 
    (
    select ',max(case when LevelName = ' + QUOTENAME(LevelName, '''') + N'then Amount end) as [Amount_' + LevelName + ']'
    from (select distinct LevelName from #T where LevelCode in (8, 10, 20)) as T 
    order by LevelName
    for xml path('')
    ) + N' from #T group by ID;';
    
    print @sql;
    
    exec sp_executesql @sql;
    go
    drop table #T;
    go

    Nota que solo incluyo los sgtes valores de [LevelCode] (8 (10), 10 (M), 20 (S)).


    AMB

    Some guidelines for posting questions...

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



    lunes, 21 de septiembre de 2015 13:24

Todas las respuestas

  • no se si estamos en el mismo asunto creo que sí, puedes hacerlo dinámico de esta forma.

    /*
    create table temp (id int, amount numeric(10,2), levelname char(2),levelcode int)
    insert into temp 
    select 1017207  ,  120.00,    '10',       8
    union all
    select 1017207   , 180.00,     '70' ,      9
     union all
    select 1017207  ,  150.00 ,   ' M'   ,    10
     union all
    select 1017207  ,  200.00  ,  ' S'    ,   20
    */
    
    declare @columns nvarchar(max)
    select @columns=STUFF((
    				select ','+quotename(levelcode) from temp for xml path('')),1,1,'')
    declare @levelname nvarchar(max)
    select @levelname=STUFF((
    				select ','+''''+(levelname)+'''' from temp for xml path('')),1,1,'')
    declare @sql nvarchar(max)
    set @sql=N'Select ID,' +@columns + ' From (Select ID,Amount,levelcode from TEMP Where LevelName in (' +@levelname +' ) ) as TempT
     PIVOT(Max(Amount) For levelcode in ( '+@columns +' )) As PivotT';
      exec sp_executesql @sql,N''


    Comparte lo que sepas, aprende lo que no sepas (FGG)
    portalSQL
    El rincón del DBA

    domingo, 20 de septiembre de 2015 9:34
    Moderador
  • Por que quieres hacer el query de forma dinamica si los valores a pivotear son fijos y conocidos antes de la ejecucion?

    Para realizar la operacion de pivoteo se necesitan los sgtes pasos:

    - Agrupar (GROUP BY)

    - Regar valores (CASE expression)

    - Agregar (MIN / MAX / SUM / etc.)

    Esto se puede hacer con una sentencia SELECT que agrupe por ID, lo mismo que haria el operador PIVOT por detras del telon. La diferencia fundamental entre un metodo y/o el otro es que con el operador PIVOT solo se puede transformar una columna a la vez.

    declare @T table (
    ID int,
    Amount decimal(6, 2),
    LevelName varchar(10),
    LevelCode int
    );
    
    insert into @T 
    	(ID, Amount, LevelName, LevelCode)
    values
    	(1017207, 120.00, '10', 8),
    	(1017207, 180.00, '70', 9),
    	(1017207, 150.00, 'M', 10),
    	(1017207, 200.00, 'S', 20),
    
    	(1017208, 220.00, '10', 8),
    	(1017208, 280.00, '70', 9),
    	(1017208, 250.00, 'M', 10),
    	(1017208, 300.00, 'S', 20);
    
    select
    	ID,
    	MAX(case when LevelName = '10' then Amount end) as Amount_10,
    	MAX(case when LevelName = 'M' then Amount end) as Amount_M,
    	MAX(case when LevelName = 'S' then Amount end) as Amount_S
    from
    	@T
    group by
    	ID;
    go

    Si incistes en hacerlo dinamicamente, cosa que no veo necesaria, aca te dejo como hacerlo de manera simple.

    create table #T (
    ID int,
    Amount decimal(6, 2),
    LevelName varchar(10),
    LevelCode int
    );
    
    insert into #T 
    	(ID, Amount, LevelName, LevelCode)
    values
    	(1017207, 120.00, '10', 8),
    	(1017207, 180.00, '70', 9),
    	(1017207, 150.00, 'M', 10),
    	(1017207, 200.00, 'S', 20),
    
    	(1017208, 220.00, '10', 8),
    	(1017208, 280.00, '70', 9),
    	(1017208, 250.00, 'M', 10),
    	(1017208, 300.00, 'S', 20);
    
    declare 
    	@sql nvarchar(max);
    
    set @sql = N'
    select ID' + 
    (
    select ',max(case when LevelName = ' + QUOTENAME(LevelName, '''') + N'then Amount end) as [Amount_' + LevelName + ']'
    from (select distinct LevelName from #T where LevelCode in (8, 10, 20)) as T 
    order by LevelName
    for xml path('')
    ) + N' from #T group by ID;';
    
    print @sql;
    
    exec sp_executesql @sql;
    go
    drop table #T;
    go

    Nota que solo incluyo los sgtes valores de [LevelCode] (8 (10), 10 (M), 20 (S)).


    AMB

    Some guidelines for posting questions...

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



    lunes, 21 de septiembre de 2015 13:24
  • Saludos,

    Puedes probar con un Pivot dinámico tal como muestro en mi blog,

    Trata así:

    DECLARE @nombres varchar(MAX);
    DECLARE @columnas varchar(MAX);
    DECLARE @sql nvarchar(MAX);
    
    CREATE TABLE #Table 
    (
    id int,
    amount numeric(18,7), 
    levelname varchar(2),
    levelcode int
    );
    
    INSERT INTO #Table(id, amount, levelname, levelcode)
    VALUES
    	(1017207, 120.00, '10', 8),
    	(1017207, 180.00, '70', 9),
    	(1017207, 150.00, 'M', 10),
    	(1017207, 200.00, 'S', 20);
    
     SET @nombres = STUFF(    
     (    
     SELECT    
       ',' + QUOTENAME(LTRIM(levelname)) + ' AS Amount_' + CONVERT(varchar(10),levelname)
     FROM    
       (SELECT DISTINCT levelname
        FROM #Table
       ) AS T    
     FOR XML PATH('')    
     ), 1, 1, '');   
    
    
     SET @columnas = STUFF(    
     (    
     SELECT    
       ',' + QUOTENAME(LTRIM(levelname))    
     FROM    
       (SELECT DISTINCT levelname
        FROM #Table
       ) AS T    
     FOR XML PATH('')    
     ), 1, 1, '');    
    
     SET @sql = N'    
     SELECT    
       ID, ' + @nombres + N'
      FROM    
      (     
      SELECT  ID, levelname, amount
      FROM #Table
      ) AS T
      PIVOT    
      (    
      MAX(amount)    
      FOR levelname IN (' + @columnas + N')    
      ) AS P;';
           
    EXEC sp_executesql @sql;    
    DROP TABLE #Table;



    Ayacucho - Perú
    Recuerda si mi solución atiende tu consulta por favor márcala como útil y como respuesta.

    http://litigiouslobo.blogspot.com/
    El Blog de Steve Morrison

    • Editado Nathán XS lunes, 21 de septiembre de 2015 15:08
    lunes, 21 de septiembre de 2015 13:39