Transformações de linhas em colunas (e vice-versa), relatórios de matriz e pivôs dinâmicos.
SUM(CASE...)) e avaliar desempenho.GROUP BY e noções de dynamic SQL.
PIVOT transforma linhas em colunas, consolidando valores por um agregador (ex.: SUM/COUNT).
UNPIVOT faz o inverso, útil para normalizar dados “wide” para “long”.
SUM, AVG, COUNT, etc. Escolha conforme o contexto.-- Estrutura (resumo)
SELECT <colunas_linha>, [colunaPivot1], [colunaPivot2], ...
FROM (
SELECT <linha>, <coluna_pivot>, <valor>
FROM <fonte>
) AS src
PIVOT (
<AGREGADOR>(<valor>)
FOR <coluna_pivot> IN ([colunaPivot1], [colunaPivot2], ...)
) AS p;
-- Exemplo: total por Produto x Mês
-- Fonte "long" (Produto, Mes, Valor)
WITH Fonte AS (
SELECT Produto, DATENAME(MONTH, DataVenda) AS Mes, Valor
FROM dbo.VendasProdutos
)
SELECT Produto, [Janeiro], [Fevereiro], [Março], [Abril], [Maio], [Junho],
[Julho], [Agosto], [Setembro], [Outubro], [Novembro], [Dezembro]
FROM Fonte
PIVOT (SUM(Valor) FOR Mes IN ([Janeiro],[Fevereiro],[Março],[Abril],[Maio],[Junho],
[Julho],[Agosto],[Setembro],[Outubro],[Novembro],[Dezembro])) AS p
ORDER BY Produto;
QUOTENAME() ao gerar dinamicamente.SUM(CASE...)-- Equivalente com SUM(CASE WHEN ... THEN Valor ELSE 0 END)
SELECT
Produto,
SUM(CASE WHEN Mes = 'Janeiro' THEN Valor ELSE 0 END) AS Janeiro,
SUM(CASE WHEN Mes = 'Fevereiro' THEN Valor ELSE 0 END) AS Fevereiro,
SUM(CASE WHEN Mes = 'Março' THEN Valor ELSE 0 END) AS Março
FROM dbo.VendasProdutosPorMes
GROUP BY Produto;
STRING_AGG para gerar colunas via SQL dinâmico.-- Exemplo: tabela "wide" com colunas por mês → normalizar para (Produto, Mes, Valor)
SELECT Produto, Mes, Valor
FROM (
SELECT Produto, [Jan], [Fev], [Mar]
FROM dbo.VendasProdutosWide
) AS w
UNPIVOT (
Valor FOR Mes IN ([Jan], [Fev], [Mar])
) AS u;
CASE Mes WHEN 'Jan' THEN 'Janeiro' ...) ou converter tipos conforme necessário.Quando a lista de colunas a pivotar é desconhecida (ex.: meses presentes nos dados), gere a consulta dinamicamente.
DECLARE @cols nvarchar(max);
DECLARE @sql nvarchar(max);
-- 1) Extrair colunas dinamicamente (ex.: meses distintos)
SELECT @cols = STRING_AGG(QUOTENAME(Mes), ',')
FROM (SELECT DISTINCT DATENAME(MONTH, DataVenda) AS Mes FROM dbo.VendasProdutos) AS x;
-- 2) Montar a query PIVOT
SET @sql = N'
SELECT Produto, ' + @cols + N'
FROM (
SELECT Produto, DATENAME(MONTH, DataVenda) AS Mes, Valor
FROM dbo.VendasProdutos
) AS src
PIVOT (SUM(Valor) FOR Mes IN (' + @cols + N')) AS p
ORDER BY Produto;';
-- 3) Executar
EXEC sp_executesql @sql;
QUOTENAME() nos nomes de coluna e nunca concatene entradas do usuário sem validação → risco de SQL Injection em SQL dinâmico.(Produto, Mes)).NULL × 0 surpresas: use ISNULL() na apresentação, se desejado.-- Limpar e criar tabelas de exemplo
IF OBJECT_ID('dbo.VendasProdutos','U') IS NOT NULL DROP TABLE dbo.VendasProdutos;
IF OBJECT_ID('dbo.VendasProdutosWide','U') IS NOT NULL DROP TABLE dbo.VendasProdutosWide;
GO
CREATE TABLE dbo.VendasProdutos(
IdVenda int IDENTITY PRIMARY KEY,
DataVenda date NOT NULL,
Produto nvarchar(50) NOT NULL,
UF char(2) NULL,
Valor decimal(10,2) NOT NULL
);
-- Amostra
INSERT INTO dbo.VendasProdutos (DataVenda, Produto, UF, Valor) VALUES
('2025-01-10', N'Pneu A', 'SP', 1200.00),
('2025-01-21', N'Pneu B', 'SP', 750.00),
('2025-02-05', N'Pneu A', 'PR', 900.00),
('2025-02-18', N'Pneu C', 'SP', 500.00),
('2025-03-02', N'Pneu B', 'RJ', 650.00),
('2025-03-15', N'Pneu A', 'SP', 1100.00),
('2025-08-01', N'Pneu A', 'SP', 800.00),
('2025-08-10', N'Pneu B', 'SP', 999.90);
-- Tabela "wide" para UNPIVOT
CREATE TABLE dbo.VendasProdutosWide(
Produto nvarchar(50) PRIMARY KEY,
[Jan] decimal(10,2) NULL,
[Fev] decimal(10,2) NULL,
[Mar] decimal(10,2) NULL
);
INSERT INTO dbo.VendasProdutosWide (Produto, [Jan],[Fev],[Mar]) VALUES
(N'Pneu A', 1200.00, 900.00, 1100.00),
(N'Pneu B', 750.00, NULL, 650.00);
GO
WITH Fonte AS (
SELECT Produto, DATENAME(MONTH, DataVenda) AS Mes, Valor
FROM dbo.VendasProdutos
)
SELECT Produto, [Janeiro], [Fevereiro], [Março], [Agosto]
FROM Fonte
PIVOT (SUM(Valor) FOR Mes IN ([Janeiro],[Fevereiro],[Março],[Agosto])) AS p
ORDER BY Produto;
DECLARE @cols nvarchar(max);
DECLARE @sql nvarchar(max);
SELECT @cols = STRING_AGG(QUOTENAME(DATENAME(MONTH, DataVenda)), ',')
FROM (SELECT DISTINCT DataVenda FROM dbo.VendasProdutos) AS d;
SET @sql = N'
WITH Fonte AS (
SELECT Produto, DATENAME(MONTH, DataVenda) AS Mes, Valor
FROM dbo.VendasProdutos
)
SELECT Produto, ' + @cols + N'
FROM Fonte
PIVOT (SUM(Valor) FOR Mes IN (' + @cols + N')) AS p
ORDER BY Produto;';
EXEC sp_executesql @sql;
SELECT Produto, Mes, Valor
FROM dbo.VendasProdutosWide
UNPIVOT (
Valor FOR Mes IN ([Jan],[Fev],[Mar])
) AS u
ORDER BY Produto, Mes;
WITH Fonte AS (
SELECT Produto, DATENAME(MONTH, DataVenda) AS Mes, Valor
FROM dbo.VendasProdutos
)
SELECT
Produto,
SUM(CASE WHEN Mes='Janeiro' THEN Valor ELSE 0 END) AS Janeiro,
SUM(CASE WHEN Mes='Fevereiro' THEN Valor ELSE 0 END) AS Fevereiro,
SUM(CASE WHEN Mes='Março' THEN Valor ELSE 0 END) AS Março,
SUM(CASE WHEN Mes='Agosto' THEN Valor ELSE 0 END) AS Agosto
FROM Fonte
GROUP BY Produto
ORDER BY Produto;
Produto nas colunas por UF (SP, RJ, PR).T1..T4 usando DATEPART(QUARTER).STRING_AGG + QUOTENAME.VendasProdutosWide em formato normalizado e some por mês.SUM(CASE) em vez de PIVOT.-- 1) Produto × UF
WITH F AS (
SELECT Produto, UF, Valor FROM dbo.VendasProdutos
)
SELECT Produto, [SP], [RJ], [PR]
FROM F
PIVOT (SUM(Valor) FOR UF IN ([SP],[RJ],[PR])) AS p
ORDER BY Produto;
-- 2) Por Trimestre
WITH F AS (
SELECT Produto, DATEPART(QUARTER, DataVenda) AS T, Valor
FROM dbo.VendasProdutos
)
SELECT Produto, [1] AS T1, [2] AS T2, [3] AS T3, [4] AS T4
FROM F
PIVOT (SUM(Valor) FOR T IN ([1],[2],[3],[4])) AS p
ORDER BY Produto;
-- 3) PIVOT dinâmico por mês
DECLARE @cols nvarchar(max), @sql nvarchar(max);
SELECT @cols = STRING_AGG(QUOTENAME(DATENAME(MONTH, DataVenda)), ',')
FROM (SELECT DISTINCT DataVenda FROM dbo.VendasProdutos) AS d;
SET @sql = N'
WITH F AS (
SELECT Produto, DATENAME(MONTH, DataVenda) AS Mes, Valor FROM dbo.VendasProdutos
)
SELECT Produto, ' + @cols + N' FROM F
PIVOT (SUM(Valor) FOR Mes IN (' + @cols + N')) AS p
ORDER BY Produto;';
EXEC sp_executesql @sql;
-- 4) UNPIVOT + soma por mês
WITH N AS (
SELECT Produto, Mes, Valor
FROM dbo.VendasProdutosWide
UNPIVOT (Valor FOR Mes IN ([Jan],[Fev],[Mar])) AS u
)
SELECT Mes, SUM(Valor) AS TotalMes
FROM N
GROUP BY Mes;
-- 5) Produto × UF com SUM(CASE)
SELECT
Produto,
SUM(CASE WHEN UF='SP' THEN Valor ELSE 0 END) AS SP,
SUM(CASE WHEN UF='RJ' THEN Valor ELSE 0 END) AS RJ,
SUM(CASE WHEN UF='PR' THEN Valor ELSE 0 END) AS PR
FROM dbo.VendasProdutos
GROUP BY Produto
ORDER BY Produto;
PIVOT (obrigatório).QUOTENAME().PIVOT preencha NULL com 0 (use ISNULL()).QUOTENAME e validou entradas?SUM(CASE) como alternativa?