SQL 如何将行转置为列(按关键变量分组)?

8

我将尝试按照唯一标识符(CASE_ID)进行分组,将行转置为列。

我有一个具有以下结构的表:

CASE_ID   AMOUNT   TYPE  
100         10       A  
100         50       B  
100         75       A  
200         33       B  
200         10       C  

我正在尝试查询它以生成这个结构...
| CASE_ID | AMOUNT1 | TYPE1 | AMOUNT2 | TYPE2 | AMOUNT3 |  TYPE3 |
|---------|---------|-------|---------|-------|---------|--------|
|     100 |      10 |     A |      50 |     B |      75 |      A |
|     200 |      33 |     B |      10 |     C |  (null) | (null) |

假设有一个更大的数据集,其中CASE_ID、TYPE和AMOUNT具有大量可能的值。

我尝试使用透视表,但我不需要聚合函数(只是尝试重新组织数据)。现在我正在尝试以某种方式使用row_number,但不确定如何使用。

我基本上正在尝试复制一个名为Casestovars的SPSS命令,但需要能够在SQL中执行。谢谢。


搜索动态枢轴,你会找到很多答案。这与聚合不太相关,而是关于创建正确的列名和其中的值。 - Kyle Hale
2个回答

13

通过使用row_number()创建一个连续的数字,然后再使用带有CASE表达式的聚合函数即可获得结果:

select case_id,
  max(case when seq = 1 then amount end) amount1,
  max(case when seq = 1 then type end) type1,
  max(case when seq = 2 then amount end) amount2,
  max(case when seq = 2 then type end) type2,
  max(case when seq = 3 then amount end) amount3,
  max(case when seq = 3 then type end) type3
from 
(
  select case_id, amount, type,
    row_number() over(partition by case_id
                      order by case_id) seq
  from yourtable
) d
group by case_id;

请参阅带有演示的SQL Fiddle

如果您正在使用具有PIVOT函数的数据库产品,则可以首先对amounttype列进行解析,再使用row_number()与PIVOT一起使用。在SQL Server中,有限数量值的基本语法如下:

select case_id, amount1, type1, amount2, type2, amount3, type3
from
(
  select case_id, col+cast(seq as varchar(10)) as col, value
  from 
  (
    select case_id, amount, type,
      row_number() over(partition by case_id
                        order by case_id) seq
    from yourtable
  ) d
  cross apply
  (
    select 'amount', cast(amount as varchar(20)) union all
    select 'type', type
  ) c (col, value)
) src
pivot
(
  max(value)
  for col in (amount1, type1, amount2, type2, amount3, type3)
) piv;

请参阅带演示的SQL Fiddle

如果您有未知数量的值,则可以使用动态SQL来获得结果 - SQL Server语法如下:

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

select @cols = STUFF((SELECT ',' + QUOTENAME(col+cast(seq as varchar(10))) 
                    from
                    (
                      select row_number() over(partition by case_id
                                                order by case_id) seq
                      from yourtable
                    ) d
                    cross apply
                    (
                      select 'amount', 1 union all
                      select 'type', 2
                    ) c (col, so)
                    group by col, so
                    order by seq, so
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT case_id,' + @cols + ' 
             from 
             (
                select case_id, col+cast(seq as varchar(10)) as col, value
                from 
                (
                  select case_id, amount, type,
                    row_number() over(partition by case_id
                                      order by case_id) seq
                  from yourtable
                ) d
                cross apply
                (
                  select ''amount'', cast(amount as varchar(20)) union all
                  select ''type'', type
                ) c (col, value)
            ) x
            pivot 
            (
                max(value)
                for col in (' + @cols + ')
            ) p '

execute sp_executesql @query;

请查看附有演示的SQL Fiddle。每个版本都将呈现以下结果:

| CASE_ID | AMOUNT1 | TYPE1 | AMOUNT2 | TYPE2 | AMOUNT3 |  TYPE3 |
|---------|---------|-------|---------|-------|---------|--------|
|     100 |      10 |     A |      50 |     B |      75 |      A |
|     200 |      33 |     B |      10 |     C |  (null) | (null) |

0

以下是在BigQuery中的解决方案:

DECLARE cols STRING;
DECLARE query STRING;

SET cols = (
  SELECT STRING_AGG(DISTINCT CONCAT(column, CAST(seq AS STRING)), ',')
  FROM (
    SELECT column, ROW_NUMBER() OVER(PARTITION BY case_id ORDER BY case_id) AS seq
    FROM yourtable
    CROSS JOIN UNNEST(['amount', 'type']) AS column
  )
);

SET query = (
  SELECT CONCAT(
    'SELECT case_id, ', cols, ' ',
    'FROM (',
    '  SELECT case_id, CONCAT(column, CAST(seq AS STRING)) AS column, value ',
    '  FROM (',
    '    SELECT case_id, amount, type, ',
    '      ROW_NUMBER() OVER(PARTITION BY case_id ORDER BY case_id) AS seq ',
    '    FROM yourtable',
    '  ) ',
    '  CROSS JOIN UNNEST(["amount", "type"]) AS column ',
    '  CROSS JOIN (',
    '    SELECT "amount" AS column, CAST(amount AS STRING) AS value FROM yourtable UNION ALL ',
    '    SELECT "type" AS column, type AS value FROM yourtable ',
    '  ) ',
    '  WHERE column = CONCAT(column, CAST(seq AS STRING))',
    ') ',
    'PIVOT(MAX(value) FOR column IN (', cols, '))'
  )
);

EXECUTE IMMEDIATE query;

目前你的回答不够清晰,请编辑并添加更多细节,以帮助其他人理解它如何回答问题。你可以在帮助中心找到有关如何撰写好答案的更多信息。 - Community

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接