Pivot/transpose 多列有效地将行转化为列

Pivot/transpose rows into columns efficiently with multiple columns

假设我有这样的数据:

  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

然而,即使现在查询 returns 两行,每个帐户都有 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 实现吗?

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

这个问题是要对数据进行透视需要聚合。在此,您的列的值 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;

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;

为此你需要像 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
Num1 | Type1 | Code | Group |   123 |    234
:--- | :---- | :--- | :---- | ----: | -----:
1X2  | GG    | XX1  | INTS  | 75.00 | 100.00

db<>fiddle here