Comprender la función PIVOT en T-SQL

82

Soy muy nuevo en SQL.

Tengo una mesa como esta:

ID | TeamID | UserID | ElementID | PhaseID | Effort
-----------------------------------------------------
1  |   1    |  1      |   3       |  5     |   6.74
2  |   1    |  1      |   3       |  6     |   8.25
3  |   1    |  1      |   4       |  1     |   2.23
4  |   1    |  1      |   4       |  5     |   6.8
5  |   1    |  1      |   4       |  6     |   1.5

Y me dijeron que obtuviera datos como este

ElementID | PhaseID1 | PhaseID5 | PhaseID6
--------------------------------------------
    3     |   NULL   |   6.74   |   8.25
    4     |   2.23   |   6.8    |   1.5

Entiendo que necesito usar la función PIVOT. Pero no puedo entenderlo con claridad. Sería de gran ayuda si alguien pudiera explicarlo en el caso anterior (o cualquier alternativa, si corresponde).

Nosotros somos
fuente

Respuestas:

108

Se PIVOTutiliza para rotar los datos de una columna a varias columnas.

Para su ejemplo, aquí hay un Pivot ESTÁTICO, lo que significa que codifica las columnas que desea rotar:

create table temp
(
  id int,
  teamid int,
  userid int,
  elementid int,
  phaseid int,
  effort decimal(10, 5)
)

insert into temp values (1,1,1,3,5,6.74)
insert into temp values (2,1,1,3,6,8.25)
insert into temp values (3,1,1,4,1,2.23)
insert into temp values (4,1,1,4,5,6.8)
insert into temp values (5,1,1,4,6,1.5)

select elementid
  , [1] as phaseid1
  , [5] as phaseid5
  , [6] as phaseid6
from
(
  select elementid, phaseid, effort
  from temp
) x
pivot
(
  max(effort)
  for phaseid in([1], [5], [6])
)p

Aquí hay una demostración de SQL con una versión funcional.

Esto también se puede hacer a través de un PIVOT dinámico donde crea la lista de columnas de forma dinámica y realiza el PIVOT.

DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX);

select @cols = STUFF((SELECT distinct ',' + QUOTENAME(c.phaseid) 
            FROM temp c
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT elementid, ' + @cols + ' from 
            (
                select elementid, phaseid, effort
                from temp
           ) x
            pivot 
            (
                 max(effort)
                for phaseid in (' + @cols + ')
            ) p '


execute(@query)

Los resultados para ambos:

ELEMENTID   PHASEID1    PHASEID5    PHASEID6
3           Null        6.74        8.25
4           2.23        6.8         1.5
Taryn
fuente
1
Gracias lo tengo. Lo único que necesito codificar PhaseIDantes de QUOTENAME. ¿Correcto?
Web-E
1
en el QUOTENAME debe identificar de qué columna necesita obtener los valores. ¿Es eso lo que preguntas?
Taryn
Para que la solución STUFF funcione con nombres de columna extraños (espacios, paréntesis, etc.) tuve que hacerlo SELECT distinct '],[', y también al final de la declaración1, 2, '') + ']'
Nat
@ Web-E, lamentablemente sí. Como solución alternativa, puede escribir la cadena de consulta en su aplicación o jugar con SQL dinámico en un procedimiento almacenado.
MarcoM
7

Estos son el ejemplo de pivote muy básico, por favor revise eso.

SQL SERVER - Ejemplos de tablas PIVOT y UNPIVOT

Ejemplo del enlace anterior para la tabla de productos:

SELECT PRODUCT, FRED, KATE
FROM (
SELECT CUST, PRODUCT, QTY
FROM Product) up
 PIVOT (SUM(QTY) FOR CUST IN (FRED, KATE)) AS pvt
ORDER BY PRODUCT

rinde:

 PRODUCT FRED  KATE
 --------------------
 BEER     24    12
 MILK      3     1
 SODA   NULL     6
 VEG    NULL     5

Se pueden encontrar ejemplos similares en la publicación del blog Tablas dinámicas en SQL Server. Una simple muestra

Shaikh Farooque
fuente
También tenga en cuenta que si extrae una columna numérica adicional de la tabla de origen, el pivote divide los resultados en muchas filas. Ejemplo SELECT CUST, VEG, SODA FROM (SELECT rand() as x, CUST, PRODUCT, QTY FROM Product) up PIVOT ( SUM(x) FOR PRODUCT IN (VEG, SODA) ) AS pvt ORDER BY CUST GO Para que esto funcione, debe eliminar la qtycolumna de la fuente
Raheel Hasan
4

Tengo algo que agregar aquí que nadie mencionó.

La pivotfunción funciona muy bien cuando la fuente tiene 3 columnas: una para elaggregate , otra para distribuir como columnas fory otra como pivote para la rowdistribución. En el ejemplo de producto es QTY, CUST, PRODUCT.

Sin embargo, si tiene más columnas en la fuente, dividirá los resultados en varias filas en lugar de una fila por pivote en función de valores únicos por columna adicional (como Group Bylo haría en una consulta simple).

Vea este ejemplo, he agregado una columna de marca de tiempo a la tabla de origen:

ingrese la descripción de la imagen aquí

Ahora mira su impacto:

SELECT CUST, MILK

FROM Product
-- FROM (SELECT CUST, Product, QTY FROM PRODUCT) p
PIVOT (
    SUM(QTY) FOR PRODUCT IN (MILK)
) AS pvt

ORDER BY CUST

ingrese la descripción de la imagen aquí


Para solucionar este problema, puede extraer una subconsulta como fuente como todos han hecho anteriormente, con solo 3 columnas (esto no siempre funcionará para su escenario, imagine si necesita poner un where condición para la marca de tiempo).

La segunda solución es usar ay group byhacer una suma de los valores de la columna pivotada nuevamente.

SELECT 
CUST, 
sum(MILK) t_MILK

FROM Product
PIVOT (
    SUM(QTY) FOR PRODUCT IN (MILK)
) AS pvt

GROUP BY CUST
ORDER BY CUST

GO

ingrese la descripción de la imagen aquí

Raheel Hasan
fuente
4

Un pivote se usa para convertir una de las columnas en su conjunto de datos de filas en columnas (esto generalmente se conoce como la columna de expansión ). En el ejemplo que ha dado, esto significa convertir las PhaseIDfilas en un conjunto de columnas, donde hay una columna para cada valor distinto quePhaseID puede contener: 1, 5 y 6 en este caso.

Estos valores pivotados se agrupan a través de la ElementIDcolumna del ejemplo que ha proporcionado.

Por lo general, también debe proporcionar alguna forma de agregación que le proporcione los valores a los que hace referencia la intersección del valor de propagación ( PhaseID) y el valor de agrupación ( ElementID). Aunque en el ejemplo dado, la agregación que se utilizará no está clara, pero involucra la Effortcolumna.

Una vez que se realiza este pivote, las columnas de agrupación y distribución se utilizan para encontrar un valor de agregación . O en su caso, ElementIDy PhaseIDXbusque Effort.

Al utilizar la terminología de agrupación, difusión y agregación , normalmente verá una sintaxis de ejemplo para un pivote como:

WITH PivotData AS
(
    SELECT <grouping column>
        , <spreading column>
        , <aggregation column>
    FROM <source table>
)
SELECT <grouping column>, <distinct spreading values>
FROM PivotData
    PIVOT (<aggregation function>(<aggregation column>)
        FOR <spreading column> IN <distinct spreading values>));

Esto brinda una explicación gráfica de cómo las columnas de agrupación, distribución y agregación se convierten de la fuente a tablas dinámicas si eso ayuda aún más.

t_warsop
fuente
3

Para configurar el error de compatibilidad

use esto antes de usar la función pivote

ALTER DATABASE [dbname] SET COMPATIBILITY_LEVEL = 100  
Easvarr
fuente
3
    SELECT <non-pivoted column>,
    [first pivoted column] AS <column name>,
    [second pivoted column] AS <column name>,
    ...
    [last pivoted column] AS <column name>
FROM
    (<SELECT query that produces the data>)
    AS <alias for the source query>
PIVOT
(
    <aggregation function>(<column being aggregated>)
FOR
[<column that contains the values that will become column headers>]
    IN ( [first pivoted column], [second pivoted column],
    ... [last pivoted column])
) AS <alias for the pivot table>
<optional ORDER BY clause>;

USE AdventureWorks2008R2 ;
GO
SELECT DaysToManufacture, AVG(StandardCost) AS AverageCost 
FROM Production.Product
GROUP BY DaysToManufacture;

    DaysToManufacture          AverageCost
0                          5.0885
1                          223.88
2                          359.1082
4                          949.4105

    -- Pivot table with one row and five columns
SELECT 'AverageCost' AS Cost_Sorted_By_Production_Days, 
[0], [1], [2], [3], [4]
FROM
(SELECT DaysToManufacture, StandardCost 
    FROM Production.Product) AS SourceTable
PIVOT
(
AVG(StandardCost)
FOR DaysToManufacture IN ([0], [1], [2], [3], [4])
) AS PivotTable;




Here is the result set.
Cost_Sorted_By_Production_Days    0         1         2           3       4       
AverageCost                       5.0885    223.88    359.1082    NULL    949.4105
usuario2211290
fuente
1
¿Por qué la <SELECT query that produces the data>mesa no es justa?
Raheel Hasan