DBAtlas · Atlas de operações de banco de dados

PIVOT / UNPIVOT (linhas↔colunas)

Oracle

-- Turn sales by month into columns
SELECT * FROM (
  SELECT month, amount FROM sale
)
PIVOT (SUM(amount) FOR month IN ('Jan' AS jan, 'Feb' AS feb, 'Mar' AS mar));

-- UNPIVOT: turn columns back into rows
SELECT product, month, amount
FROM sale_wide
UNPIVOT (amount FOR month IN (jan AS 'Jan', feb AS 'Feb'));

PIVOT transforma valores distintos das linhas em colunas (uma tabela cruzada), e UNPIVOT transforma colunas de volta em linhas; usados em tabelas de resumo/relatório. O Oracle oferece os operadores PIVOT/UNPIVOT nativos desde o 11g; a lista de colunas deve ser estática (colunas dinâmicas exigem PIVOT XML ou código).

PostgreSQL

-- The tablefunc extension for crosstab
CREATE EXTENSION IF NOT EXISTS tablefunc;
SELECT * FROM crosstab(
  'SELECT product, month, amount FROM sale ORDER BY 1,2'
) AS ct(product text, jan numeric, feb numeric, mar numeric);

-- Without an extension: conditional aggregation
SELECT product,
  SUM(amount) FILTER (WHERE month = 'Jan') AS jan,
  SUM(amount) FILTER (WHERE month = 'Feb') AS feb
FROM sale GROUP BY product;

O PostgreSQL não tem operador PIVOT nativo. Há duas formas: a função crosstab() da extensão tablefunc ou a agregação condicional portável (SUM(...) FILTER (WHERE ...) ou com CASE). A cláusula FILTER é uma técnica de pivot elegante, específica do PostgreSQL.

MySQL

-- There is no PIVOT; conditional aggregation with manual CASE
SELECT product,
  SUM(CASE WHEN month = 'Jan' THEN amount ELSE 0 END) AS jan,
  SUM(CASE WHEN month = 'Feb' THEN amount ELSE 0 END) AS feb,
  SUM(CASE WHEN month = 'Mar' THEN amount ELSE 0 END) AS mar
FROM sale
GROUP BY product;

O MySQL/MariaDB não têm operador PIVOT/UNPIVOT; uma tabela cruzada é montada manualmente com GROUP BY mais um SUM(CASE WHEN ... THEN ... END) para cada coluna de destino. Se as colunas forem dinâmicas, a consulta é gerada como string com um prepared statement.

SQL Server

SELECT * FROM (
  SELECT product, month, amount FROM sale
) src
PIVOT (
  SUM(amount) FOR month IN ([Jan], [Feb], [Mar])
) pvt;

-- Converting back with UNPIVOT is also supported

O SQL Server suporta nativamente os operadores PIVOT e UNPIVOT desde 2005. A lista de colunas do PIVOT é informada como valores estáticos entre colchetes; para colunas dinâmicas, a consulta é montada com SQL dinâmico. Para resumos pequenos, a agregação condicional (CASE) é uma alternativa.

MongoDB

// PIVOT equivalent: conditional aggregation inside $group
db.sale.aggregate([
  { $group: {
      _id: "$product",
      jan: { $sum: { $cond: [ { $eq: ["$month", "Jan"] }, "$amount", 0 ] } },
      feb: { $sum: { $cond: [ { $eq: ["$month", "Feb"] }, "$amount", 0 ] } }
  } }
])

O MongoDB não tem operador PIVOT; uma tabela cruzada é montada no estágio $group com $sum + $cond (agregação condicional) para cada coluna de destino. Para o sentido inverso (UNPIVOT), os campos são expandidos em linhas com $objectToArray e $unwind.

Redis

# Not applicable: Redis has no pivot/cross tab.

O Redis não tem o conceito de girar linhas e colunas. Resumos/tabelas cruzadas são calculados no lado da aplicação; resumos solicitados com frequência podem ser pré-calculados e armazenados em campos de Hash (um campo por coluna). O FT.AGGREGATE do RediSearch pode fazer agregação agrupada, mas não é um pivot real.

Elasticsearch

# Closest equivalent: nested terms aggregations
GET sale/_search
{
  "size": 0,
  "aggs": { "product": { "terms": { "field": "product" },
    "aggs": { "month": { "terms": { "field": "month" },
      "aggs": { "total": { "sum": { "field": "amount" } } } } } }
}

O Elasticsearch não tem operador PIVOT; um resumo cruzado é montado com terms aggregations aninhadas (chave externa / chave interna), o resultado volta como buckets aninhados em vez de colunas e é transformado em tabela no cliente. Um job de transform pode gerar um índice de destino agrupado, semelhante a um pivot.

Abrir no DBAtlas →