Principales respuestas
Consultas PIVOT en SQL Server

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 PivotTEste query me resulta así:
ID 8 10 20
1017207 120.00 150.00 200.00Que 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 PivotTAsi 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!
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- Marcado como respuesta Miguel Egea GómezMVP, Moderator miércoles, 23 de septiembre de 2015 15:27
-
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
- Editado HunchbackMVP lunes, 21 de septiembre de 2015 13:49
- Propuesto como respuesta Willams Morales lunes, 21 de septiembre de 2015 19:27
- Marcado como respuesta Miguel Egea GómezMVP, Moderator miércoles, 23 de septiembre de 2015 15:28
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- Marcado como respuesta Miguel Egea GómezMVP, Moderator miércoles, 23 de septiembre de 2015 15:27
-
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
- Editado HunchbackMVP lunes, 21 de septiembre de 2015 13:49
- Propuesto como respuesta Willams Morales lunes, 21 de septiembre de 2015 19:27
- Marcado como respuesta Miguel Egea GómezMVP, Moderator miércoles, 23 de septiembre de 2015 15:28
-
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