Sql server 理解T-SQL中的PIVOT函数
我对SQL非常陌生 我有一张这样的桌子: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
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)