使用 PIVOT 时未在 SQL Server 2012 中获得正确的 SUM 值

Not getting the correct SUM values in SQL Server 2012 when using a PIVOT

我正在尝试创建一个查询,该查询将以某些行为中心,但将 SUM 某些列,然后将它们组合在一起。我之前使用过 PIVOT 函数,但当我的结果集包含相似值时,我 运行 遇到了问题。

这是 SQL Server 2012。

示例代码:

CREATE TABLE #Foo
(
     Store varchar(50), 
     Employee varchar(50), 
     Sold money, 
     Waste money, 
     Tmsp datetime
)

INSERT INTO #Foo 
VALUES
   ('Harrisburg', 'John', 20.00, 10.00, GETDATE()),
   ('Harrisburg', 'John', 20.00, 10.00, GETDATE()),
   ('Harrisburg', 'Jim', 20.00, 10.00, GETDATE()),
   ('Seattle', 'Jim', 20.00, 10.00, GETDATE()),
   ('Seattle', 'Alex', 20.00, 10.00, GETDATE())

SELECT 
    Store,
    SUM(Sold) TotalSold,
    SUM([John]) WastedByJohn,
    SUM([Jim]) WastedByJim,
    SUM([Alex]) WastedByAlex
FROM 
    #Foo
PIVOT
    (SUM(Waste)
     FOR Employee IN ([John], [Jim], [Alex])
    ) PVT
GROUP BY 
   Store

DROP TABLE #Foo

这会产生以下结果:

Store      | TotalSold | WastedByJohn | WastedByJim | WastedByAlex
Harrisburg | 20.00     | 20.00        | 10.00       | NULL
Seattle    | 20.00     | NULL         | 10.00       | 10.00

根据 table 中的数据,Harrisburg 的 TotalSold 和 Seattle 的 TotalSold 不应该是 60.00 吗?

我越来越难以理解,因为如果我更改数据使值不相同,我会得到正确的结果。

INSERT INTO #Foo 
VALUES
    ('Harrisburg', 'John', 25.00, 10.00, GETDATE()),
    ('Harrisburg', 'John', 30.00, 10.00, GETDATE()),
    ('Harrisburg', 'Jim', 40.00, 10.00, GETDATE()),
    ('Seattle', 'Jim', 50.00, 10.00, GETDATE()),
    ('Seattle', 'Alex', 60.00, 10.00, GETDATE())

这组数据产生了预期的结果:

Store      | TotalSold | WastedByJohn | WastedByJim | WastedByAlex
Harrisburg | 95.00     | 20.00        | 10.00       | NULL
Seattle    | 110.00    | NULL         | 10.00       | 10.00

我环顾四周,找不到关于为什么 PIVOT 在聚合时会基于不同值而不同的答案。我觉得我在这里缺少一些基本的东西,除非我碰巧遇到 SQL 服务器的一些问题,这不太可能。

如有任何帮助,我们将不胜感激。

谢谢!

以下查询应该可以满足您的需求:

SELECT Store,
       TotalSold,
       [John] AS WastedByJohn,
       [Jim] AS WastedByJim,
       [Alex] AS WastedByAlex
FROM (SELECT Store, Employee, Waste,
             SUM(Sold) OVER (PARTITION BY Store) AS TotalSold
      FROM #Foo) src
PIVOT
    (SUM(Waste)
     FOR Employee IN ([John], [Jim], [Alex])
    ) PVT

要了解为什么会出现意外结果,请尝试不使用 GROUP BY 子句的查询:

SELECT Store, Sold, [John], [Jim], [Alex]
FROM 
    #Foo
PIVOT
    (SUM(Waste)
     FOR Employee IN ([John], [Jim], [Alex])
    ) PVT

输出:

Store       Sold    John    Jim     Alex
Harrisburg  20,00   20,00   10,00   NULL
Seattle     20,00   NULL    10,00   10,00

现在,用第二个版本的示例数据再次尝试相同的操作:

输出:

Store       Sold    John    Jim     Alex
Harrisburg  25,00   10,00   NULL    NULL
Harrisburg  30,00   10,00   NULL    NULL
Harrisburg  40,00   NULL    10,00   NULL
Seattle     50,00   NULL    10,00   NULL
Seattle     60,00   NULL    NULL    10,00

通过比较 2 个不同的结果集,您可以清楚地看到 PIVOT 发生在未参与其中的每个列组合,即 StoreSold 的每个组合.

在第一种情况下只有 Harrisburg,20,00Seattle,20,00。这就是为什么在这种情况下你只能得到两行。在第二种情况下,您总共有 3 + 2 = 5 种组合。

您现在可以明白为什么 GROUP BY 只适用于第二种情况了。

您没有了解数据透视表的含义。让我解释。首先有3个要素:传播、聚合和分组。 传播是你在列中得到的,即 Employee IN ([John], [Jim], [Alex])。聚合为 SUM(Waste)。那么什么是分组元素呢?最后一个是通过消除列来确定的。 IE。每列,但聚合和传播。在您的示例中,它将是 Store, Sold, Tps。它将按这 3 列对数据进行分组。但你不想要这个。您只想按 Store 分组。那么该怎么办?我可以建议使用条件聚合:

SELECT 
    Store,
    SUM(Sold) TotalSold,
    SUM(CASE WHEN Employee = 'John' THEN Waste ELSE 0 END) WastedByJohn,
    SUM(CASE WHEN Employee = 'Jim' THEN Waste ELSE 0 END) WastedByJim,
    SUM(CASE WHEN Employee = 'Alex' THEN Waste ELSE 0 END) WastedByAlex
FROM #Foo
GROUP BY Store