Recursos Rollup e Cube do T-SQL no SQL Server 2012

Fala galera, no dia 06/07/2012 fui palestrar no TDC2012 em São Paulo na trilha de Banco de Dados, e, assistindo a apresentação do Wagner Crivelini (Artigos SQL Server Central | Artigos DevMedia | Twitter) sobre Geração de Relatórios com Linguagem SQL, baseado no padrão ISO, percebi que algumas pessoas não conheciam as instruções de CUBE ou ROLLUP que ele apresentou. Mesmo com a boa explicacão do Wagner, e seus exemplos práticos, o pessoal parecia ter dúvidas.

Por esse motivo, decidi escrever esse post explicando estes recursos dentro do SQL Server e mostrando códigos T-SQL.

Um ponto importante sobre o CUBE e o ROLLUP é que são aplicados ao SQL Server exclusivamente com a cláusula GROUP BY, como acontece com o HAVING.

Group By

Esta função é conhecida por todos (assim espero) e não vou “chover no molhado” explicando seu funcionamento. O que vou fazer aqui é colocar o código T-SQL que usei e o resultado obtido, para servir de comparação com os outros códigos que, aí sim, é o objetivo do artigo.

Este código T-SQL será base para os exemplos e faz parte da tabela PurchaseOrderDetail do database de exemplo AdventureWorks2012. Este código representa o somatório de cada pedido, agrupado pelo mês e ano.

   1:  -- 34 registros
   2:  select sum(LineTotal) [Total], month(DueDate) [Mes], year(DueDate) [Ano]
   3:  from [Purchasing].[PurchaseOrderDetail]
   4:  where year(DueDate) >= 2005
   5:  group by month(DueDate), year(DueDate)
   6:  order by [Ano],[Mes]

Sendo o resultado:

Total Mes Ano
9491,518 5 2005
94404,3 6 2005
299240 1 2006
50781,36 2 2006
927072,2 3 2006
649928,9 4 2006
346917,6 5 2006
106491 6 2006
171578,4 7 2006
483826,6 8 2006
249488,5 9 2006
375543,6 10 2006
281325,4 11 2006
283939,4 12 2006
131485,8 3 2007
123167,8 5 2007
749096,6 6 2007
719047,5 7 2007
117332 8 2007
2756843 9 2007
4956873 10 2007
2211744 11 2007
1925624 12 2007
4066161 1 2008
4031461 2 2008
4739842 3 2008
4829902 4 2008
5373580 5 2008
5459623 6 2008
5790441 7 2008
7400999 8 2008
4077396 9 2008
327,1575 10 2008
1020 11 2008

Função Rollup

Esta função gera resultados de subtotal baseado nos grupos das colunas definidos no Group By, resultando mais algumas linhas ao resultset. Estas linhas extras são os subtotais que foram calculados, sendo uma linha para cada subtotal e uma linha para o total final (somatório de todos os subtotais). Neste exemplo de código, o Group By convencional retornou 34 registros. Já com o Rollup o retorno foi de 47. Isso ocorre, porque o calculo foi baseado na coluna Mes que adicionou 12 subtotais ao resultset + 1 coluna calculando o total de todos os subtotais calculados.

   1:  -- 47 registros
   2:  select sum(LineTotal) [Total], month(DueDate) [Mes], year(DueDate) [Ano]
   3:  from [Purchasing].[PurchaseOrderDetail]
   4:  where year(DueDate) >= 2005
   5:  group by rollup (month(DueDate), year(DueDate))
 

O resultado é parecido com o do Group By, porém possui valores NULL inseridos em determinadas linhas. Esses valores NULL indicam que naquela linha do resultado existe um subtotal calculado, e um onde pode-se encontrar dois valores NULL na linha, significa que é o somatório do resultado de todos os subtotais.

Repare que onde existe o valor NULL na coluna Ano, a coluna Mes está preenchida com o valor do mês, isso significa que o resultado da coluna Total é o somatório de todos os meses referentes ao valor da coluna Mes, independente do ano.

Total Mes Ano
299240 1 2006
4066161 1 2008
4365401 1 NULL
50781,36 2 2006
4031461 2 2008
4082243 2 NULL
927072,2 3 2006
131485,8 3 2007
4739842 3 2008
5798400 3 NULL
649928,9 4 2006
4829902 4 2008
5479831 4 NULL
9491,518 5 2005
346917,6 5 2006
123167,8 5 2007
5373580 5 2008
5853157 5 NULL
94404,3 6 2005
106491 6 2006
749096,6 6 2007
5459623 6 2008
6409615 6 NULL
171578,4 7 2006
719047,5 7 2007
5790441 7 2008
6681067 7 NULL
483826,6 8 2006
117332 8 2007
7400999 8 2008
8002157 8 NULL
249488,5 9 2006
2756843 9 2007
4077396 9 2008
7083727 9 NULL
375543,6 10 2006
4956873 10 2007
327,1575 10 2008
5332744 10 NULL
281325,4 11 2006
2211744 11 2007
1020 11 2008
2494090 11 NULL
283939,4 12 2006
1925624 12 2007
2209563 12 NULL
63791995 NULL NULL

Função Cube

Esta função gera ainda mais algumas linhas de subtotais que a Rollup, pois ela calcula todos os subtotais possíveis para o agrupamento, e não somente o subtotal de uma coluna. No caso do Rollup acima, foi calculado o subtotal dos meses, já no Cube foi calculado o subtotal dos meses e também o subtotal dos anos. Totalizando 51 registros (34 registros do Group By + 12 registros dos subtotais de meses + 4 subtotais de anos + 1 subtotal de TUDO = 51 registros)

   1:  -- 51 registros
   2:  select sum(LineTotal) [Total], month(DueDate) [Mes], year(DueDate) [Ano]
   3:  from [Purchasing].[PurchaseOrderDetail]
   4:  where year(DueDate) >= 2005
   5:  group by cube (month(DueDate), year(DueDate))
 

O resultado é parecido com o do Rollup porém com mais subtotais, calculando não somente os subtotais dos meses como também os subtotais dos anos, e também o somatório de todos os subtotais. Na mesma explicação que temos no Rollup, quando existem valores NULL na coluna Mes significa que o valor existente na coluna Total é relativo ao ano representado na coluna Ano. Mesma situação continua se aplicado às colunas Mes, quando se encontra valor NULL na coluna Ano.

Total Mes Ano
9491,518 5 2005
94404,3 6 2005
103895,8 NULL 2005
299240 1 2006
50781,36 2 2006
927072,2 3 2006
649928,9 4 2006
346917,6 5 2006
106491 6 2006
171578,4 7 2006
483826,6 8 2006
249488,5 9 2006
375543,6 10 2006
281325,4 11 2006
283939,4 12 2006
4226133 NULL 2006
131485,8 3 2007
123167,8 5 2007
749096,6 6 2007
719047,5 7 2007
117332 8 2007
2756843 9 2007
4956873 10 2007
2211744 11 2007
1925624 12 2007
13691214 NULL 2007
4066161 1 2008
4031461 2 2008
4739842 3 2008
4829902 4 2008
5373580 5 2008
5459623 6 2008
5790441 7 2008
7400999 8 2008
4077396 9 2008
327,1575 10 2008
1020 11 2008
45770752 NULL 2008
63791995 NULL NULL
4365401 1 NULL
4082243 2 NULL
5798400 3 NULL
5479831 4 NULL
5853157 5 NULL
6409615 6 NULL
6681067 7 NULL
8002157 8 NULL
7083727 9 NULL
5332744 10 NULL
2494090 11 NULL
2209563 12 NULL

 

Repare que o crescimento de linhas no resultset é exponencial, baseado na quantidade de agrupadores que são gerados pelo Group By. Isso significa que você precisa se preocupar com os dados, pois o processamento destes subtotais cruzados (como acontece na função Cube) pode impactar negativamente a performance da sua consulta, uma vez que o SQL Server deverá cruzar os somatórios de todas as possibilidades existentes em todas as colunas.

Sobre Diego Nogare 350 Artigos
Diego Nogare é Gerente Técnico de Engenharia de Machine Learning no Itaú-Unibanco. Também é professor em programas de pós graduação no Mackenzie e na FIAP, em São Paulo. Foi nomeado como Microsoft MVP por 11 anos seguidos, e hoje faz parte do programa Microsoft Regional Director.