为啥我们在 SQL Server 中透视文本列时使用 Max 函数?

Posted

技术标签:

【中文标题】为啥我们在 SQL Server 中透视文本列时使用 Max 函数?【英文标题】:Why is the Max function used when we pivot text columns in SQL Server?为什么我们在 SQL Server 中透视文本列时使用 Max 函数? 【发布时间】:2018-06-13 04:12:57 【问题描述】:

我刚刚学会了如何在 SQL Server 中进行数据透视。我想知道为什么要旋转文本列时使用max 函数?这背后的逻辑是什么?我知道是CountSum 等(因为您将相应的行和列相加)但我不明白当我们有文本列时使用max 的逻辑?

比如我的代码是:

SELECT * 
  FROM ( SELECT DATE
               ,SITA
               ,EVENT 
          FROM  [UKRMC].[dbo].[strategy] 
          where datename(year, DATE) = 2018 or datename(year,DATE)=2019
        ) strategy
  PIVOT ( max(EVENT)
          FOR SITA IN ([ABZPD],[BFSPD]
,[BFSZH]
,[BHXPD]
,[BHXZH]
,[BRSZH]
,[BRUPQ] ) piv

【问题讨论】:

这是因为必须使用聚合函数。在许多情况下,只有一个值,max 选择该一条记录,同时仍遵循透视语法规则。 顺便说一句where datename(year, DATE) = 2018 or datename(year,DATE)=2019 并不理想,因为它可能会导致non-SARGable 查询计划,即不会命中列DATE 上的任何潜在索引。 在这些情况下,最好在 SQL 中使用类似 SINGLE_VALUE 的聚合! 【参考方案1】:

因为在您的示例中,您选择了 EVENT 作为在 PIVOT 交叉点中显示的值(即,由于您在 PIVOT 子句中指定了 EVENT),因此必须使用以下之一指定该值permissible aggregate functions,因为您在数据透视表中选择的每个列值可能有多行,当按剩余列分组时(即您的案例中的 DATE)。

在 Sql Server[1] 中,MAX()MIN() 通常用于旋转非数字列,因为它能够显示列的原始值之一.

任何非聚合和非透视列都将保持原样,并将用于形成透视所基于的组(在您的情况下,列 DATE 既不在聚合中,或者列支点,因此它将形成行组)

考虑您的透视表包含与您的谓词匹配的多行的情况,例如:

INSERT INTO strategy (DATE, SITA, EVENT) VALUES
('1 Jan 2018', 'ABZPD', 'Event1'),
('1 Jan 2018', 'BFSPD', 'Event2'),
('1 Jan 2018', 'BFSPD', 'Event3');

枢轴后:

DATE                    ABZPD   BFSPD
2018-01-01T00:00:00Z    Event1  Event3

即在 Pivot 期间,Event2Event3BFSPD 行需要以某种方式投影到单个单元格中 - 因此需要聚合。即使已知只有 one value,仍然需要此聚合(在上面的示例中,SITA ABZPDEvent1 值就是这种情况)。

由于BFSPD 有两个事件,您需要以某种方式解决如何将值投影到单个单元格值中。在 VARCHAR 列上使用 MAX 可在多行投影到同一结果枢轴“单元格”的情况下解析“最大”值 (Event3) - SqlFiddle example here

您可以选择使用 COUNT(Event) 向您显示每行/枢轴交叉点的事件数 - Fiddle

您可以使用DATE 切换EVENT 上的聚合 - 因此EVENT 在column grouping 中使用。


*1 像 AVGSTDEV 这样的聚合显然不适用于字符串。其他 RDBMS 有额外的聚合,例如 FIRST,它将任意取第一个值,或 GROUP_CONCAT / LIST_AGG,它可以将字符串值与分隔符折叠在一起。而PostGres 允许您制作自己的聚合函数!。但遗憾的是,SqlServer 中没有这些,因此 MIN() /MAX() 现在。

【讨论】:

【参考方案2】:

使用PIVOT 命令时必须指定聚合函数,因为透视操作的第一步是对FOR 子句中指定的列进行分组操作,以减少结果表的行数。

聚合函数用于管理输出表中所需的其他列的值。

来自Technet 文档:

PIVOT 通过旋转唯一值来旋转表值表达式 从表达式中的一列到输出中的多列, 和 在任何剩余的地方执行聚合 最终输出中需要的列值

这是来自同一篇 Technet 文章的PIVOT 命令语法:

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

请注意,PIVOT 子句之后必须指定聚合函数:

...
<aggregation function>(<column being aggregated>)
...

有关此主题的更多见解,另请参阅this Microsoft Press 文章。

【讨论】:

以上是关于为啥我们在 SQL Server 中透视文本列时使用 Max 函数?的主要内容,如果未能解决你的问题,请参考以下文章

带有文本值的 SQL Server 数据透视表

为啥 Django 在添加新列时会删除 SQL DEFAULT 约束?

了解为啥有大量文本列时按查询分组会变慢

在数据透视期间指定列时标识符无效 - ORA-00904

在 VB 2010 的数据表中插入新列时,如何更新 SQL Server 2008 数据库?

当 WHERE 子句中只有一列时,SQL Server 会使用复合索引吗?