Sql server 理解T-SQL中的PIVOT函数

Sql server 理解T-SQL中的PIVOT函数,sql-server,sql-server-2008,tsql,pivot,Sql Server,Sql Server 2008,Tsql,Pivot,我对SQL非常陌生 我有一张这样的桌子: ID | TeamID | UserID | ElementID | PhaseID | Effort ----------------------------------------------------- 1 | 1 | 1 | 3 | 5 | 6.74 2 | 1 | 1 | 3 | 6 | 8.25 3 | 1 | 1

我对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函数。但我不明白。
如果有人能在上面的例子中解释一下,那将是非常有帮助的。如果有任何替代方案,请仔细阅读这些是非常基本的pivot示例

产品表的上述链接示例:

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
类似的例子可以在blog post中找到,该blog post用于将数据从一列旋转到多列

对于您的示例,这里是一个静态轴,这意味着您需要硬编码要旋转的列:

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
这是一个有工作版本的

这也可以通过动态透视来完成,您可以在动态透视中创建列列表并执行透视

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

设置兼容性错误的步骤

在使用pivot函数之前使用此选项

ALTER DATABASE [dbname] SET COMPATIBILITY_LEVEL = 100  

我想在这里补充一些没人提到的东西

当源有3列时,pivot函数非常有效:一列用于聚合,一列作为列展开,另一列作为行分布的轴心。在产品示例中,它是数量、客户、产品

但是,如果源中有更多列,它将根据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条件

第二种解决方案是使用GROUPBY并再次对数据透视列的值求和

SELECT 
CUST, 
sum(MILK) t_MILK

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

GROUP BY CUST
ORDER BY CUST

GO

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

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

通常,您还需要提供某种形式的聚合,以提供扩展值PhaseID和分组值ElementID的交集引用的值。虽然在给定的示例中,将使用的聚合不清楚,但涉及到“努力”列

完成此数据透视后,将使用分组列和扩展列查找聚合值。或者在您的情况下,ElementID和PhaseIDX查找工作

使用分组、扩展和聚合术语,您通常会看到pivot的示例语法:

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>));
以图形方式解释分组、扩展和聚合列如何从源表转换为数据透视表(如果有帮助)。

可能无法在Microsoft Azure Synapse Service上工作。一种可能的替代方法是,采用@Taryn动态生成的cols方法,通过使用获得相同的结果


谢谢,明白了。我唯一需要在QUOTENAME之前硬编码PhaseID的东西。对吗?在QUOTENAME中,您必须确定需要从哪个列获取值。这就是你要问的吗?为了使STUFF解决方案能够处理奇怪的列名、空格、括号等,我必须选择不同的“]、[”,并且在语句1、2、+']@Web-E的末尾,不幸的是,是的。作为一种解决方法,您可以在应用程序中编写查询字符串,也可以在存储过程中使用动态SQL。为什么不只是表?还要注意,如果您从源表中提取额外的数字列,则pivot会将结果分成多行。示例:选择客户、蔬菜、苏打水从选择兰德为x、客户、产品、数量从产品到透视汇总对于蔬菜中的产品,苏打水为pvt订单按客户GO为使其工作,您必须从源中删除数量列
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>));
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)