【问题标题】:Pivot/transpose rows into columns efficiently with multiple columns使用多列有效地将行旋转/转置为列
【发布时间】:2021-06-21 15:50:42
【问题描述】:

假设我有看起来像这样的数据:

  Table
  Num1       Type1       Code       Group      DA        Account      Value
  1X2        GG          XX1        INTS       1         123          75.00
  1X2        GG          XX1        INTS       1         234          100.00

我要做的是透视数据,使其看起来像这样:

  Num1       Type1       Code       Group      DA      123        234
  1X2        GG          XX1        INTS       1       75.00      100.00

我不太确定如何使用 PIVOT 完成这样的事情,但我确实尝试了以下方法:

 Select Num1, 
        Type1, 
        Code, 
        Group, 
        DA, 
        '123' = (Select Value from Table t2 where t1.num1 = t2.num1 and Account = 123 ),
        '234' = (Select Value from Table t2 where t1.num1 = t2.num1 and Account = 234 )
 From Table t1
  

但是我得到的错误是:Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.

在这种情况下,我在每个子查询中添加了“Top 1”:

Select Num1, 
        Type1, 
        Code, 
        Group, 
        DA, 
        '123' = (Select TOP 1 Value from Table t2 where t1.num1 = t2.num1 and Account = 123 ),
        '234' = (Select TOP 1 Value from Table t2 where t1.num1 = t2.num1 and Account = 234 )
 From Table t1

然而,即使查询现在为每个帐户返回两行 AMOUNTS,我还没有完全理解 TOP 1 的目的,但基本上现在数据看起来像这样:

 Num1       Type1       Code       Group      DA        123        234
  1X2        GG          XX1        INTS       1        NULL       100.00 
  1X2        GG          XX1        INTS       1        75.00      NULL

我想这还不错,因为我可以在所有其他列上使用GROUP BY 执行MAX(123)MAX(234),最后得到1 行。

有没有更好的方法来解决这个问题?这可以通过 PIVOT 实现吗?

【问题讨论】:

  • 注意:语法'Literal String Alias' = {Expression} 已被弃用,不应使用。然而,理想情况下,无论如何都要避免使用文字字符串作为别名;对于刚接触这门语言的人来说,这可能会让人很困惑。
  • @Larnu - 是的,SSMS 正在为这个向我咆哮。更多的是概念证明,但如果我坚持这种方式,我会将 {Expression} 设为 '123'
  • 不,请AS [123]AS "123"
  • @Larnu - 明白了,谢谢!
  • 我很好奇你问如何使用 PIVOT 来做到这一点,我回答使用 PIVOT,你接受了答案,然后几天后你换了一个不使用 PIVOT 的答案。

标签: sql-server ssms pivot


【解决方案1】:

为此,您需要 MAX 和 GROUP BY 等聚合函数

CREATE TABLE  Table1
    ([Num1] varchar(3), [Type1] varchar(2), [Code] varchar(3), [Group] varchar(4), [DA] int, [Account] int, [Value] DECIMAL(10,2))
;
    
INSERT INTO  Table1
    ([Num1], [Type1], [Code], [Group], [DA], [Account], [Value])
VALUES
    ('1X2', 'GG', 'XX1', 'INTS', 1, 123, 75.00),
    ('1X2', 'GG', 'XX1', 'INTS', 1, 234, 100.00)
;
GO
SELECT
[Num1], [Type1], [Code], [Group],
MAX(CASE WHEN [Account] = 123 THEN [Value] ELSe -999999999999 END) AS [123],
MAX(CASE WHEN [Account] = 234 THEN [Value] ELSe -9999999999 END) AS [234]
FROM Table1
 GROUP BY  [Num1], [Type1], [Code], [Group]
GO
数字1 |类型1 |代码 |集团 | 123 | 234 :--- | :---- | :--- | :---- | ----: | -----: 1X2 | GG | XX1 |整数 | 75.00 | 100.00

db小提琴here

【讨论】:

    【解决方案2】:
    SELECT
      [Num1], 
      [Type1], 
      [Code], 
      [Group], 
      [DA], 
      [123],
      [234]
    FROM
      yourTable
    PIVOT
    (
      MAX([value])  
      FOR [account] IN ([123], [234])
    )
      AS PivotTable
    

    https://dbfiddle.uk/?rdbms=sqlserver_2019&fiddle=7fbe16b9254aa5ee60a23e43eec9597f

    【讨论】:

      【解决方案3】:

      这个问题是,要透视您需要聚合的数据。在此,您的列的值value

      SELECT Num1, 
             Type1, 
             Code, 
             [Group], --GROUP is a reserved keyword and should not be used to object names
             DA, 
             MAX(CASE Account WHEN 123 THEN [Value] END) AS [123],
             MAX(CASE Account WHEN 234 THEN [Value] END) AS [234]
      FROM dbo.Table t1
      GROUP BY Num1, 
               Type1, 
               Code, 
               [Group], --GROUP is a reserved keyword and should not be used to object names
               DA;
      

      【讨论】:

        【解决方案4】:

        MatBailie 和 Larnu 100% 正确!但是,如果您需要动态执行此操作(例如,您不知道要汇总的帐户数量),则可以使用动态 SQL。您的示例的语法如下所示:

            IF OBJECT_ID('tempdb..#acct_list') IS NOT NULL DROP TABLE #acct_list; -- Deletes #acct_list temp table if it already exists
            SELECT DISTINCT ROW_NUMBER() OVER(ORDER BY Account) rownum, Account -- Lists each account with a row number for looping
            INTO #acct_list -- Creates and inserts data into a #acct_list temp table
            FROM Table 
            ORDER BY Account;
        
            DECLARE @col INT = 1; -- Counter value used to keep track of which account is being aggregated
            DECLARE @cnt INT = (SELECT COUNT(DISTINCT Account) FROM Table); -- Counts the number of unique accounts
            DECLARE @sql NVARCHAR(MAX) = N'SELECT Num1, Type1, Code, [Group], DA'; -- Writes the SQL string to be executed
            WHILE @col <= @cnt -- Loops through columns until no more exist
                BEGIN
                    -- Identifies the current account
                    DECLARE @acct VARCHAR(3) = (SELECT Account FROM #acct_list WHERE rownum = @col);
                    -- Writes SQL syntax for the current account column
                    SET @sql += ' ,MAX(CASE Account WHEN ''' + @acct + ''' THEN Value END) [' + @acct + ']'
                    -- Increment the counter to advance to the next column
                    SET @col += 1;
                END;
            SET @sql += ' FROM Table GROUP BY Num1, Type1, Code, [Group], DA'
            --PRINT @sql
            EXEC SYS.SP_EXECUTESQL @sql;
        

        【讨论】:

        • 确保使用QUOTENAME。您可以使用STRING_AGGFOR XML 进行聚合而无需循环
        • 好消息@Charlieface!我已经将 FOR XML 用于许多其他事情,但没有想到以这种方式应用它。以后一定会用!
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-01-20
        • 2018-02-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-06-21
        相关资源
        最近更新 更多