为啥带有“额外”列的数据透视不能合并结果
Posted
技术标签:
【中文标题】为啥带有“额外”列的数据透视不能合并结果【英文标题】:Why pivot with "extra" columns doesn't combine results为什么带有“额外”列的数据透视不能合并结果 【发布时间】:2015-03-07 00:46:33 【问题描述】:我知道你们中的许多人都观察到了这种行为,但我想知道是否有人可以解释原因。当我创建一个小表来创建使用数据透视函数的示例时,我得到了我期望的结果:
CREATE TABLE dbo.AverageFishLength
(
Fishtype VARCHAR(50) ,
AvgLength DECIMAL(8, 2) ,
FishAge_Years INT
)
INSERT INTO dbo.AverageFishLength
( Fishtype, AvgLength, FishAge_Years )
VALUES ( 'Muskie', 32.75, 3 ),
( 'Muskie', 37.5, 4 ),
( 'Muskie', 39.75, 5 ),
( 'Walleye', 16.5, 3 ),
( 'Walleye', 18.25, 4 ),
( 'Walleye', 20.0, 5 ),
( 'Northern Pike', 20.75, 3 ),
( 'Northern Pike', 23.25, 4 ),
( 'Northern Pike', 26.0, 5 );
这是透视查询:
SELECT Fishtype ,
[3] AS [3 Years Old] ,
[4] AS [4 Years Old] ,
[5] AS [5 Years Old]
FROM dbo.AverageFishLength PIVOT( SUM(AvgLength)
FOR FishAge_Years IN ( [3], [4], [5] ) ) AS PivotTbl
结果如下:
但是,如果我创建带有标识列的表,则结果会分成不同的行:
DROP TABLE dbo.AverageFishLength
CREATE TABLE dbo.AverageFishLength
(
ID INT IDENTITY(1,1) ,
Fishtype VARCHAR(50) ,
AvgLength DECIMAL(8, 2) ,
FishAge_Years INT
)
INSERT INTO dbo.AverageFishLength
( Fishtype, AvgLength, FishAge_Years )
VALUES ( 'Muskie', 32.75, 3 ),
( 'Muskie', 37.5, 4 ),
( 'Muskie', 39.75, 5 ),
( 'Walleye', 16.5, 3 ),
( 'Walleye', 18.25, 4 ),
( 'Walleye', 20.0, 5 ),
( 'Northern Pike', 20.75, 3 ),
( 'Northern Pike', 23.25, 4 ),
( 'Northern Pike', 26.0, 5 );
完全相同的查询:
SELECT Fishtype ,
[3] AS [3 Years Old] ,
[4] AS [4 Years Old] ,
[5] AS [5 Years Old]
FROM dbo.AverageFishLength PIVOT( SUM(AvgLength)
FOR FishAge_Years IN ( [3], [4], [5] ) ) AS PivotTbl
不同的结果:
在我看来,查询中正在使用 ID 列,即使它根本没有出现在查询中。这几乎就像它隐式包含在查询中,但未显示在结果集中。
谁能解释为什么会这样?
【问题讨论】:
我应该补充一点,我知道如何绕过它,使用子查询或 row_number... 我真的只是对它发生的原因感兴趣。 这个问题让我饿了 【参考方案1】:发生这种情况是因为 ID
列对于每一行都是唯一的,并且因为您正在直接查询表(没有子查询),所以该列包含在聚合函数所需的 GROUP BY
中。
MSDN docs about FROM
的文档声明如下:
table_source PIVOT <pivot_clause>
指定 table_source 基于 pivot_column 进行旋转。 table_source 是一个表或表表达式。输出是一个表,其中包含 table_source 的所有列,除了 pivot_column 和 value_column。 table_source 的列,除了 pivot_column 和 value_column 之外,被称为枢轴运算符的分组列。
PIVOT
对输入表执行关于分组列的分组操作,并返回每组一行。此外,对于出现在 input_table 的 pivot_column 中的 column_list 中指定的每个值,输出都包含一列。
您的版本基本上是在说SELECT * FROM yourtable
和 PIVOT 该数据。即使ID
列不在您的最终SELECT 列表中,它也是查询中的一个分组元素。如果您将 PIVOT 与“pre-PIVOT”示例进行比较以显示您将看到您的版本。此示例使用 CASE 表达式和聚合函数:
SELECT Fishtype,
sum(case when FishAge_Years = 3 then AvgLength else 0 end) as [3],
sum(case when FishAge_Years = 4 then AvgLength else 0 end) as [4],
sum(case when FishAge_Years = 5 then AvgLength else 0 end) as [5]
FROM dbo.AverageFishLength
GROUP BY Fishtype, ID;
结果会出现偏差,因为即使您在最终列表中没有 ID
,它仍然被用于分组,并且由于它们是唯一的,因此您会得到多行。
使用 PIVOT 时解决此问题的最简单方法是使用子查询:
SELECT Fishtype ,
[3] AS [3 Years Old] ,
[4] AS [4 Years Old] ,
[5] AS [5 Years Old]
FROM
(
SELECT Fishtype,
AvgLength,
FishAge_Years
FROM dbo.AverageFishLength
) d
PIVOT
(
SUM(AvgLength)
FOR FishAge_Years IN ( [3], [4], [5] )
) AS PivotTbl;
在此版本中,您只返回表中实际需要和想要的列 - 这不包括 ID
,因此它不会用于对您的数据进行分组。
【讨论】:
好的,我明白了。如果它在语法中更透明,或者如果您可以通过从 SELECT 中排除某些列而不是必须执行子查询来从表中排除某些列,那就太好了。感谢您的详尽解释。 @Dave.Gugg 它实际上在文档中,请参阅我的编辑。它隐藏在FROM
的文档中。以上是关于为啥带有“额外”列的数据透视不能合并结果的主要内容,如果未能解决你的问题,请参考以下文章