在 SQL 中使用“Pivot”将行转换为列

Posted

技术标签:

【中文标题】在 SQL 中使用“Pivot”将行转换为列【英文标题】:Convert Rows to columns using 'Pivot' in SQL 【发布时间】:2020-12-01 23:26:06 【问题描述】:

我已经阅读了 MS 数据透视表上的内容,但我仍然无法正确解决这个问题。

我对这些结果有看法

Invoice Date        Basecard    Item      Quantity  Subtotal    Taxes   Total
713938  09/11/2020  C90001      Desktop   14            2800       448      3248
713938  09/11/2020  C90001      Laptop    18.5      29091.25    4654.6  33745.85

我希望它以数据透视表的形式出现,如下所示:

Invoice Date        Basecard    Laptop  Desktop   Subtotal  Taxes   Total
713938  09/11/2020  C90001      18.5    14        31891.25  5102.6  36993.85

这是我的查询

SELECT * FROM (
    SELECT Invoice, Date, BaseCard, Item, sum(Quantity) Qty, sum(Subtotal) Subtotal
    FROM MyView
    Group by Invoice, Date, BaseCard, Item, Subtotal
    
) Resultados
PIVOT (
        Sum(Qty)
        FOR Item
        IN (
            [Laptop], [Desktop]         
        )
) AS PivorTable
Group by Invoice, Date, BaseCard, Laptop, Desktop, Subtotal

结果

Invoice Date      baseCard  subtotal    Laptop  Desktop
713938  2020-11-09 C90001   2800.00     NULL    14.000000
713938  2020-11-09 C90001   7076.25     4.50    NULL
713938  2020-11-09 C90001   22015.000   14.00   NULL

【问题讨论】:

【参考方案1】:

假设你有这样一张桌子

CREATE TABLE [InvoiceInfo]( 
                           [Invoice]  INT, 
                           [Date]     DATE, 
                           [Basecard] VARCHAR(100), 
                           [Item]     VARCHAR(100),
                           [Quantity] FLOAT,
                           [Price]    FLOAT
                          )

您从中创建视图。考虑到这一点,我可以建议您使用条件聚合来简化您的数据透视,并根据当前存在的[Item] 值转换为动态格式以使其灵活,例如

DECLARE @cols  AS NVARCHAR(MAX),  @query AS NVARCHAR(MAX)

SET @cols = ( SELECT STRING_AGG(
                     CONCAT('SUM(CASE WHEN [Item]=''',[Item],
                            ''' THEN [Quantity] END) AS [',[Item],']'),',') 
                FROM (SELECT DISTINCT [Item]
                        FROM [InvoiceInfo] ) i );

SET @query = CONCAT(
    N'SELECT [Invoice], [Date], [BaseCard],', @cols , 
    N'      ,SUM([Price]*[Quantity]) AS [Subtotal],
             SUM(ROUND([Price]*[Quantity]*.16, 2)) AS [Taxes],
             SUM([Price]*[Quantity])+SUM(ROUND([Price]*[Quantity]*.16, 2)) AS [Total]
        FROM [InvoiceInfo] i
       GROUP BY [Invoice], [Date], [BaseCard]');

EXEC sp_executesql @query; 

Invoice Date        BaseCard    Desktop Laptop  Subtotal    Taxes   Total
713938  2020-11-09  C90001      14      18.5    31891.25    5102.6  36993.85

更新:根据您最近声明的旧数据库版本,您可以使用以下代码,包括 FOR XML PATHSTUFF 来替代 STRING_AGG

DECLARE @cols AS NVARCHAR(MAX),  @query AS NVARCHAR(MAX);
                        
SELECT @cols = 
       STUFF((SELECT DISTINCT ',' + 
                     CONCAT('SUM(CASE WHEN [Item]=''',[Item],
                                            ''' THEN [Quantity] END) AS [',[Item],']') 
                  AS formulas
                FROM 
                (
                 SELECT DISTINCT [Item]
                   FROM [InvoiceInfo] f
                ) ff
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'');
       
SET @query = CONCAT(
    N'SELECT [Invoice], [Date], [BaseCard],', @cols , 
    N'      ,SUM([Price]*[Quantity]) AS [Subtotal],
             SUM(ROUND([Price]*[Quantity]*.16, 2)) AS [Taxes],
             SUM([Price]*[Quantity])+SUM(ROUND([Price]*[Quantity]*.16, 2)) AS [Total]
        FROM [InvoiceInfo] i
       GROUP BY [Invoice], [Date], [BaseCard]');

EXEC sp_executesql @query; 

Demo

【讨论】:

感谢您的帮助 Barbaros,但我有 SQL Server 2015 并且在运行查询时出现此错误。 -- STRING_AGG 不是可识别的内置函数名称 -- CONCAT 不是可识别的内置函数名称 嗨@Andros,不客气。由于您的数据库版本,我已更新。 对不起 Babaros,我的错误,我的 SQL 版本是 2005。我试图让它工作,但我做不到。你帮了大忙【参考方案2】:

您也只需要总结这些关键数据。您的行已拆分,因为您已将 subtotal 列添加到分组依据。

db<>fiddle

with a as (
  select 713938 as invoice, convert(date, '2020-11-09', 23) as dt, 'C90001' as Basecard, 'Desktop' as item, 14 as quantity, 2800 as subtotal, 448 as taxes, 3248 as total
  union all
  select 713938, convert(date, '2020-11-09', 23), 'C90001', 'Laptop', 18.5, 29091.25, 4654.6, 33745.85
)
select
  invoice,
  dt,
  basecard,
  sum([Desktop]) as desctop,
  sum([Laptop]) as laptop,
  sum(subtotal) as subtotal,
  sum(taxes) as taxes,
  sum(total) as total
from a
pivot (
  sum(quantity) for item in ([Desktop], [Laptop])
) as q
group by
  invoice,
  dt,
  basecard

invoice | dt         | basecard | desctop | laptop | subtotal |  taxes |    total
------: | :--------- | :------- | ------: | -----: | -------: | -----: | -------:
 713938 | 2020-11-09 | C90001   |    14.0 |   18.5 | 31891.25 | 5102.6 | 36993.85

【讨论】:

感谢您的帮助,这几乎可以完美运行,如何转换为动态格式以使其灵活?因为我有更多的项目 @Andros 没有办法在静态 SQL 中使其动态化(我认为因为解析器在执行之前应该知道确切的列名)。因此,唯一的方法是使用动态 SQL,正如另一个答案中所述。还有很多例子:one、two 和,糟糕但有效,从第二个得到,three。

以上是关于在 SQL 中使用“Pivot”将行转换为列的主要内容,如果未能解决你的问题,请参考以下文章

BigQuery 标准 SQL 如何将行转换为列

带有条件的 Oracle SQL Pivot 将行转换为列

使用 PIVOT 将行转换为列

MS Access SQL 将行转换为列

使用 PIVOT/UNPIVOT 将行转换为列

Oracle SQL Developer:如何使用 PIVOT 函数将行转置为列