90

我对 SQL 很陌生。

我有一张这样的桌子:

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

我被告知要获取这样的数据

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

我知道我需要使用 PIVOT 功能。但是不能清楚的理解。如果有人可以在上述情况下解释它,那将是很大的帮助。(或任何替代方案,如果有的话)

4

7 回答 7

116

APIVOT用于将数据从一列旋转到多列。

对于您的示例,这里是一个 STATIC Pivot,这意味着您对要旋转的列进行硬编码:

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

这是一个带有工作版本的SQL 演示。

这也可以通过动态 PIVOT 来完成,您可以在其中动态创建列列表并执行 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)

两者的结果:

ELEMENTID   PHASEID1    PHASEID5    PHASEID6
3           Null        6.74        8.25
4           2.23        6.8         1.5
于 2012-05-03T10:19:51.967 回答
8

这些是非常基本的支点示例,请仔细阅读。

SQL SERVER – PIVOT 和 UNPIVOT 表示例

产品表的上述链接示例:

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

呈现:

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

类似的示例可以在博客文章SQL Server 中的数据透视表中找到。一个简单的示例

于 2012-05-03T09:57:33.477 回答
7

我在这里添加一些没有人提到的东西。

pivot当源具有 3 列时,该函数非常有效:一列用于aggregate,一列作为带有 的列展开,一列for作为row分布的枢轴。在产品示例中,它是QTY, CUST, PRODUCT.

但是,如果源中有更多列,它将根据每个附加列的唯一值将结果分成多行而不是每个数据透视表一行(就像Group By在简单查询中所做的那样)。

看这个例子,我在源表中添加了一个时间戳列:

在此处输入图像描述

现在看看它的影响:

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

在此处输入图像描述


为了解决这个问题,您可以像上面每个人所做的那样将子查询作为源提取 - 只有 3 列(这并不总是适用于您的场景,想象一下您是否需要where为时间戳设置条件)。

第二种解决方案是使用 agroup by并再次对透视列值进行求和。

SELECT 
CUST, 
sum(MILK) t_MILK

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

GROUP BY CUST
ORDER BY CUST

GO

在此处输入图像描述

于 2019-01-09T00:30:06.563 回答
6

枢轴用于将数据集中的列之一从行转换为列(这通常称为扩展列)。在您给出的示例中,这意味着将PhaseID行转换为一组列,其中每个不同的值都有一个列,PhaseID在这种情况下可以包含 1、5 和 6。

这些透视值通过您给出的示例中的列进行分组。ElementID

通常,您还需要提供某种形式的聚合,为您提供扩展值( PhaseID) 和分组值( )的交集所引用的值ElementID。虽然在给出的示例中将使用的聚合不清楚,但涉及到Effort列。

完成此旋转后,将使用分组列和展开列来查找聚合值。或者在你的情况下,ElementIDPhaseIDX查找Effort

使用分组、传播、聚合术语,您通常会看到枢轴的示例语法为:

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>));

给出了分组、扩展和聚合列如何从源转换为透视表的图形解释,如果这有帮助的话。

于 2019-09-10T09:42:58.697 回答
4
    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
于 2016-05-14T18:40:32.937 回答
3

设置兼容性错误

在使用数据透视函数之前使用它

ALTER DATABASE [dbname] SET COMPATIBILITY_LEVEL = 100  
于 2013-09-20T06:45:23.140 回答
0

FOR XML PATH可能不适用于 Microsoft Azure Synapse Serve。一种可能的替代方法,遵循@Taryn 动态生成的 cols 方法,使用STRING_AGG获得相同的结果。

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

SELECT @cols = STRING_AGG(QUOTENAME(c.phaseid),', ')
/*OPTIONAL: within group (order by cast(t1.[FLOW_SP_SLPM] as INT) asc)*/
FROM (SELECT phaseid FROM temp
GROUP BY phaseid) c

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

execute(@query)
于 2021-02-02T16:49:22.647 回答