将两个 T-SQL 数据透视查询合二为一

Posted

技术标签:

【中文标题】将两个 T-SQL 数据透视查询合二为一【英文标题】:Combining two T-SQL pivot queries in one 【发布时间】:2009-12-14 20:14:41 【问题描述】:

假设你有这张桌子:

CREATE TABLE Records
(
    RecordId       int IDENTITY(1,1) NOT NULL,
    CreateDate     datetime          NOT NULL,
    IsSpecial      bit               NOT NULL
    CONSTRAINT PK_Records   PRIMARY KEY(RecordId)
)

现在需要创建一个报告,其中按月细分记录总数和特殊记录总数。我可以分别使用这两个查询:

--      TOTAL RECORDS PER MONTH
SELECT January, February, March, April, May, June,
    July, August, September, October, November, December
FROM (
    SELECT RecordId, DATENAME(MONTH, CreateDate) AS RecordMonth
    FROM dbo.Records
) AS SourceTable
PIVOT (
    COUNT(RecordId) FOR RecordMonth IN (January, February, March, April, May, June,
    July, August, September, October, November, December)
) AS PivotTable;

--      TOTAL SPECIAL RECORDS PER MONTH
SELECT January, February, March, April, May, June,
    July, August, September, October, November, December
FROM (
    SELECT RecordId, DATENAME(MONTH, CreateDate) AS RecordMonth
    FROM dbo.Records
    WHERE IsSpecial = 1
) AS SourceTable
PIVOT (
    COUNT(RecordId) FOR RecordMonth IN (January, February, March, April, May, June,
    July, August, September, October, November, December)
) AS PivotTable;

结果可能如下所示:

                  Jan | Feb | Mar | Apr | May | Jun | Jul | Aug | Sep | Oct | Nov | Dec
total              0     0    2     2     1      0     0    1      2     1     2    4
total special      0     0    1     0     1      0     0    0      0     0     0    2 

是否可以将这两个查询组合成一个更有效的查询?

【问题讨论】:

合并如何?你能举一个结果集的例子吗? 【参考方案1】:

我会这样做:

SELECT
     CASE SQ.total_type
          WHEN 1 THEN 'total special'
          WHEN 2 THEN 'total expensive'
          ELSE 'total'
     END AS total_type,
     SUM(CASE WHEN MONTH(R.CreateDate) = 1 THEN 1 ELSE 0 END) AS January,
     SUM(CASE WHEN MONTH(R.CreateDate) = 2 THEN 1 ELSE 0 END) AS February,
     SUM(CASE WHEN MONTH(R.CreateDate) = 3 THEN 1 ELSE 0 END) AS March,
     ...
FROM
     dbo.Records R
INNER JOIN
     (
          SELECT 0 AS total_type UNION ALL   -- All
          SELECT 1 UNION ALL                 -- IsSpecial
          SELECT 2                           -- IsExpensive
     ) AS SQ ON
     (R.IsSpecial | (R.IsExpensive * 2)) & SQ.total_type = SQ.total_type
GROUP BY
     SQ.total_type
ORDER BY
     SQ.total_type DESC

【讨论】:

Tom,您的回答非常有帮助,但我正在努力扩展此解决方案以在同一查询中包含第三个聚合。例如,如果您有另一个字段,例如 IsExpensive,并且您希望在第三个结果行中显示每月的总计。您将如何处理第三次加入? 您可以将子查询表更改为每个不同行的一组位图,每个位代表一个条件。我今天差不多完成了,但如果我想到它,我会尝试添加一个示例。 我刚刚更改了 SQL 代码,以便更轻松地处理附加条件。如果您得到另一个,只需将其添加为 2 的下一个幂(因此,“IsCheap”可能是 4)。把它放在子查询、case 语句和连接条件中,你就可以开始了。如果要更改结果的顺序,也可以更改值。 1、2 或 4 无关紧要,只要它们在整个查询中保持一致即可。【参考方案2】:

每个数据透视表只能有一个聚合 (COUNT(RecordId)),因此您只需将 UNION ALL 与一个合适的额外列组合成一个结果集,以识别每个数据透视表。

否则,您无法区分枢轴中的 2 个不同聚合

【讨论】:

我喜欢使用公用表表达式来做到这一点。【参考方案3】:

感谢汤姆的解决方案,它回答了我的关键问题。

对我来说太糟糕了,我问错了问题。对于我的问题,我现在觉得使用这样的普通分组查询会更好:

SELECT DATENAME(MONTH, CreateDate) AS Month,
    COUNT(*) AS Total,
    SUM(CASE
        WHEN IsSpecial = 1 THEN 1
        ELSE 0
    END) AS TotalSpecial,
    SUM(CASE
        WHEN IsExpensive = 1 THEN 1
        ELSE 0
    END) AS TotalExpensive
FROM Records
GROUP BY DATENAME(MONTH, CreateDate);

那么剩下要做的就是在结果出现之前对其进行旋转。很高兴知道是吗?

【讨论】:

以上是关于将两个 T-SQL 数据透视查询合二为一的主要内容,如果未能解决你的问题,请参考以下文章

T-SQL:没有聚合的数据透视表

T-SQL:透视数据(十三)

使用手动列连接取消透视 T-SQL 查询

T-SQL 动态数据透视

《MSSQL2008技术内幕:T-SQL语言基础》读书笔记(下)

《MSSQL2008技术内幕:T-SQL语言基础》读书笔记(下)