Oi! Seja bem-vindo!

Hoje veremos como fazer PIVOT com COLUNAS DINÂMICAS no SQL Server. Com essa técnica NINJA, você não precisará alterar querys quando mudarem os dados usados no PIVOT.

O que é PIVOT com COLUNAS dinâmicas?

No vídeo da última semana [https://youtu.be/mvClicAwyek] mostrei o comando PIVOT, que serve para transformar linhas em colunas no SQL Server, melhorando espetacularmente o visual de relatórios.

Se você ainda não leu o artigo da semana passada e/ou não viu o vídeo, te convido a assistir, pois nosso trabalho dessa semana é uma continuação dele.

De qualquer forma, a maior utilidade do PIVOT com colunas dinâmicas, é evitar que precisemos alterar nossos sistemas e consultas SELECT a cada vez que mudam dados no banco de dados.

Como fazer PIVOT com COLUNAS DINÂMICAS

Abaixo o script que utilizei no vídeo:


------------------------------------------------------------------
-- PIVOT: Transposição (linhas para colunas DINAMICAMENTE)
------------------------------------------------------------------
--------------------------------------------------
-- Banco de dados para nosso teste
--------------------------------------------------
-- Banco de testes
use master
if db_id('curso') is not null 
	drop database curso
go
create database curso
go
use curso
go
create table colaboradores (
	id smallint identity(1,1) primary key,
	nm varchar(100) not null,
)
insert into colaboradores values 
	('Abismail do Rosário'),
	('Josemar Pinto Godinho'),
	('Francisco Sanguessuga'),
	('Rolando da Rocha'),
	('Chaves Rosembaum'),
	('Necrotério Silveira'),
	('Navegador da Luz')

create table produtos (
	id smallint identity(1,1) primary key,
	nm varchar(100),
	vl decimal(10,2),
	ic_ativo bit not null default (1)
)
insert produtos (nm, vl) values 
	('Mouse Gammer 25 botões', 200),
	('Teclado Gammer 350 teclas', 300),
	('Monitor 32 Pol Full HD', 1000),
	('RAM DDR4 4GB Powerturbo', 500),
	('CPU Nasa 10Ghz', 2000),
	('HD SSD 1TB', 2000)

create table vendas (
	id int not null identity(1, 1) primary key,
	dt datetime not null,
	id_produto smallint not null foreign key references produtos(id),
	id_vendedor smallint foreign key references colaboradores(id),
	qt int check (qt > 0),
	vl_unitario decimal (10,2), 
	vl_venda as qt * vl_unitario
)
;with cte_seq as (
	select 1 as nr_cont
	union all
	select nr_cont + 1 
	from cte_seq where nr_cont < 10000)
insert into vendas (dt, id_produto, id_vendedor, qt, vl_unitario)
	select 
		dateadd(day, -(abs(checksum(newid())) % 1825), getdate()) dt,
		produto.id,
		colaborador.id,
		1 + (abs(checksum(newid())) % 15) nr_quantidade,
		produto.vl vl_unitario
	from cte_seq
	left join (select top 1 id, vl from produtos order by newid()) produto on 1=1
	left join (select top 1 id from colaboradores order by newid()) colaborador on 1=1
	option (maxrecursion 0)
go


--------------------------------------------------
-- Pivot padrão (com colunas fixas)
--------------------------------------------------
-- Anos em colunas:
select *
from (
	select 
		year(dt) as Ano,
		c.nm as Colaborador,
		sum(vl_venda) as Valor
	from vendas 
	join colaboradores c on c.id = vendas.id_vendedor
	group by c.nm, year(dt)
) em_linha
pivot (sum(valor) for ano in ([2012], [2013], [2014], [2015], [2016], [2017])) em_colunas 
order by 1
go

-- Colaboradores em colunas:
select *
from (
	select 
		year(dt) as Ano,
		c.nm as Colaborador,
		sum(vl_venda) as Valor
	from vendas 
	join colaboradores c on c.id = vendas.id_vendedor
	group by c.nm, year(dt)
) em_linha
pivot (sum(valor) for colaborador in ([Abismail do Rosário], [Chaves Rosembaum], [Francisco Sanguessuga])) em_colunas 
order by 1
go

--------------------------------------------------
-- Pivot ninja - colunas dinâmicas
--------------------------------------------------
-- Anos em colunas dinâmicas
declare @colunas_pivot as nvarchar(max), @comando_sql  as nvarchar(max)
set @colunas_pivot = 
	stuff((
		select 
			distinct ',' + quotename(year(dt)) 
		from vendas
		for xml path('')
	), 1, 1, '')
print @colunas_pivot
set @comando_sql = '
	select * from (
		select 
			year(dt) as Ano,
			nm as Colaborador,
			sum(vl_venda) as Valor
		from vendas
		join colaboradores c on c.id = vendas.id_vendedor
		group by nm, year(dt)
	) em_linha
	pivot (sum(valor) for ano in (' + @colunas_pivot + ')) em_colunas 
	order by 1'
print @comando_sql
execute(@comando_sql)
go

-- Colaboradores em colunas dinâmicas
declare @colunas_pivot as nvarchar(max), @comando_sql  as nvarchar(max)
set @colunas_pivot = stuff((select distinct ',' + quotename(nm) from colaboradores for xml path('')), 1, 1, '')
print @colunas_pivot
set @comando_sql = '
	select * from (
		select nm as Colaborador, year(dt) as Ano, sum(vl_venda) as Valor
		from vendas
		join colaboradores c on c.id = vendas.id_vendedor
		group by nm, dt
	) em_linha
	pivot (sum(valor) for colaborador in (' + @colunas_pivot + ')) em_colunas 
	order by 1'
print @comando_sql
execute(@comando_sql)


--------------------------------------------------
-- Excluindo dados de teste
--------------------------------------------------
use master
drop database curso

 

CONCLUSÃO

Particularmente não sou chegado a execuções indiretas de código por serem menos performáticas e mais difíceis de dar manutenção. Mas, em casos como esse, essa técnica salva a pátria, não?

Espero que tenha gostado do artigo, nos vemos no próximo!

Abraço do seu amigo Josué 🙂

 

12 respostas

  1. Bom dia!

    Quando adaptei a dica ninja não deu certo.

    O que fiz de errado ?

    declare @colunas_pivot as nvarchar(max), @comando_sql as nvarchar(max)
    set @colunas_pivot =
    stuff((
    select
    distinct ‘,’ + quotename(RIGHT(CONVERT(varchar(10),C.dt_vencto,103),7))
    from cta_pag C Join pag_lanc P
    On C.seq_pag = P.seq_pag
    for xml path(”)
    ), 1, 1, ”)
    print @colunas_pivot
    set @comando_sql = ‘

    Select * From (
    Select RIGHT(CONVERT(varchar(10),C.dt_vencto,103),7) As Mes_Ano,
    O.descricao As Conta,
    SUM(P.valor) As Valor

    From cta_pag C Join pag_lanc P
    On C.seq_pag = P.seq_pag

    Join conta O
    On O.cd_conta = P.cd_conta

    Join empresa E
    On E.cd_emp = C.cd_emp

    Join fornec F
    On F.cd_forn = C.cd_forn

    Where C.dt_vencto >=’2023-01-01’
    AND C.dt_vencto <='2023-03-31'
    AND E.nome_fant = 'KAR TRANSPORTES'

    Group By E.nome_fant,
    RIGHT(CONVERT(varchar(10),C.dt_vencto,103),7),
    O.descricao
    ) em_linhas

    pivot (sum(valor) for Mes_Ano in (' + @colunas_pivot + ') em_colunas '

    print @comando_sql
    execute(@comando_sql)

  2. Olá Josué,
    Espero que você esteja bem, agradeço por sempre ter um conteúdo de excelente qualidade.

    Como mostrou para gerar uma PIVOT com colunas dinâmicas, é possível dar nomes para essas colunas?

    Exemplo:

    Anos em colunas dinâmicas
    2022 | 2023

    Resultado esperado
    Ano de: 2022 | Ano de: 2023

    Colaboradores em colunas dinâmicas
    Abismail do Rosário | Chaves Rosembaum

    Resultado esperado
    Vendedor: Abismail do Rosário | Vendedor: Chaves Rosembaum

    1. Fala ai Igor, boa tarde!

      Obrigado meu amigo! Bem, deve haver inúmeras formas de fazer, a primeira que me ocorreu foi criar uma tabela temporária ou CTE com a sumarização dos múltiplos anos e/ou vendedores que você deseja, exemplo:

      – Criar uma tb temp ou CTE, incluindo nela a sumarização das vendas de 2022 e 2023, tipo assim:
      Anos | Valor da soma
      2020 até 2021 | R$5000
      2022 até 2023 | R$9000

      – Com essa tabela em mãos, você usa o pivot de forma semelhante a que fizemos no vídeo para apresentar o resultado, tipo assim:
      Colaborador | 2020 até 2021 | 2022 até 2023
      João da Silva | R$ 1000 | R$ 2000
      Maria Chiquinha | R$ 1500 | R$ 3000

      O mesmo poderia ser feito para sumarizar colaboradores.

      Essa é uma alternativa, não avaliei se é a melhor alternativa.

      Abs
      Josué

  3. Boa tarde!
    Josue,

    Tentei aqui mas sem sucesso, eu estou precisando trazer o resultado de um banco de dados, onde ele tem varias linhas e algumas destas precisam ser colunas de acordo com o codigo unico.

    Cada ID traz 5 linhas e preciso destas linhas Trazer para colunas para o mesmo ID..

    Teria alguma sugestão?

  4. Excelente artigo !!! Acrescentou bastante ao meu dia-a-dia

    Alguém poderia talvez me dar uma luz ?

    Tenho uma base assim

    Codfuncionario | cod_centro_custo | perc_rateio
    1 | 1 | 50%
    1 | 2 | 50%
    2 | 1 | 100%

    E preciso transformá-la na seguinte forma dinamicamente:

    codFuncionario | cod_centro_custo_1 | perc_valor1 | cod_centro_custo2 | perc_valor2 ..
    1 | 1 | 50% | 2 | 50%
    2 | 1 | 100% | NULL | NULL

    1. Opa, boa tarde!

      Valeu, seja bem-vindo ao blog! Em seu caso, talvez seja mais fácil realizar a tarefa em 2 etapas. Na primeira organiza centro de custo, na segunda adiciona ao resultset os nomes de colaboradores de forma dinâmica…

      Abs
      Josué

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *