如何在SQL Server 2008中透视数据以获得所需的结果?

时间:2015-09-04 15:06:29

标签: sql sql-server sql-server-2008 tsql pivot

过去(herehere)对此主题做出了非常好的答案。看来我仍然无法让我知道这个枢轴的东西(我也没用过很长时间)。

所以希望有人(再次)能够告诉我如何将数据转换为所需的格式:

鉴于数据:

ID | Label | Occurences | RangeBegin | RangeEnd | Unit
---+-------+------------+------------+----------+--------
1  | One   | 0          | -1000      | 0        | m
1  | One   | 5          | 0          | 10       | m
1  | One   | 8          | 10         | 20       | m
1  | One   | 6          | 20         | 30       | m
1  | One   | 15         | 30         | 40       | m
1  | One   | 0          | 40         | 1000     | m
2  | One   | 0          | -1000      | 0        | m
2  | One   | 2          | 0          | 10       | m
2  | One   | 13         | 10         | 20       | m
2  | One   | 27         | 20         | 30       | m
2  | One   | 5          | 30         | 40       | m
2  | One   | 0          | 40         | 1000     | m
1  | Two   | 0          | -1000      | 0        | kg
1  | Two   | 4          | 0          | 2        | kg
1  | Two   | 6          | 2          | 4        | kg
1  | Two   | 1          | 4          | 6        | kg
1  | Two   | 0          | 6          | 1000     | kg
2  | Two   | 0          | -1000      | 0        | kg
2  | Two   | 8          | 0          | 2        | kg
2  | Two   | 1          | 2          | 4        | kg
2  | Two   | 3          | 4          | 6        | kg
2  | Two   | 0          | 6          | 1000     | kg

期望的结果:

ID | One | OneRangeBegin | OneRangeEnd | OneUnit | Two  | TwoRangeBegin | TwoRangeEnd | TwoUnit
---+-----+---------------+-------------+---------+------+---------------+-------------+----------
 1 | 0   | -1000         | 0           | m       | 0    | -1000         | 0           | kg
 1 | 5   | 0             | 10          | m       | 4    | 0             | 2           | kg
 1 | 8   | 10            | 20          | m       | 6    | 2             | 4           | kg
 1 | 6   | 20            | 30          | m       | 1    | 4             | 6           | kg
 1 | 15  | 30            | 40          | m       | 0    | 6             | 1000        | kg
 1 | 0   | 40            | 1000        | m       | null | null          | null        | null
 2 | 0   | -1000         | 0           | m       | 0    | -1000         | 0           | kg
 2 | 2   | 0             | 10          | m       | 8    | 0             | 2           | kg
 2 | 13  | 10            | 20          | m       | 1    | 2             | 4           | kg
 2 | 27  | 20            | 30          | m       | 3    | 4             | 6           | kg
 2 | 5   | 30            | 40          | m       | 0    | 6             | 1000        | kg
 2 | 0   | 40            | 1000        | m       | null | null          | null        | null

为了使其工作更容易,我将上面的数据放在SqlFiddle

2 个答案:

答案 0 :(得分:2)

我认为可以通过这样做来实现:

  1. 根据标签拆分表格
  2. 通过对ID进行分区并根据RangeBegin进行排列来对每个表进行排名。 ROW_NUMBER() OVER(PARTITION BY ID ORDER BY RangeBegin)
  3. 完全加入ID和排名上的两个表
  4. SQL Fiddle

    SELECT 
     A.ID AS ID
    ,A.Occurences AS One
    ,A.RangeBegin AS OneRangeBegin
    ,A.RangeEnd AS OneRangeEnd
    ,A.Unit AS OneUnit
    ,B.Occurences AS Two
    ,B.RangeBegin AS TwoRangeBegin
    ,B.RangeEnd AS TwoRangeEnd
    ,B.Unit AS TwoUnit
    
    
    FROM
    (select
    *, ROW_NUMBER() OVER(PARTITION BY ID ORDER BY RangeBegin) Rank
    from
    AvailableData
    wHere Label = 'One') A
    FULL JOIN
    (select
    *, ROW_NUMBER() OVER(PARTITION BY ID  ORDER BY RangeBegin) Rank
    from
    AvailableData
    Where Label = 'Two' ) B
    ON A.ID = B.ID
    AND A.Rank = B.Rank
    ORDER BY ISNULL(A.ID, B.ID), ISNULL(A.Rank, B.Rank)
    

答案 1 :(得分:1)

我总是很难绕着枢轴缠绕我的头。出于这个原因,我倾向于使用聚合CASE方法而不是PIVOT,因为我发现它更容易推理(并且它更灵活)。基本上你需要将你的问题分解为多个步骤。

  1. 确定数据的分组方式 - 例如,ID,​​RangeBegin,RangeEnd
  2. 确定所有列的内容以及它们的来源
  3. 为每个列编写一个聚合的CASE语句 - 注意:您必须确保每个输出都是明确的,否则您将得到缺失的结果
  4. 聚合CASE枢轴的总体布局如下:

    SELECT
      grouped columns
      ,MAX(CASE WHEN condition THEN column END) as ColumnName
      ,...
    FROM
      Table
    GROUP BY grouped columns
    

    关键是上述条件将识别每列的来源。例如,当OneUnit列为Unit时,列Label显然来自One列,因此可能是:

    MAX(CASE WHEN Label='One' THEN Unit END) as OneUnit
    

    这很容易应用于您的特定示例,除非没有明显的方法将行从One关联到两个,因为您将不同的范围组合在一起。您需要确定一个明确的规则,以便在构建实际的数据透视查询之前将数据中的行组合在一起,这应该非常简单(这会生成额外的行,因为一个和两个集合之间的范围不同):

    select
      D.ID, D.RangeBegin, D.RangeEnd
      ,MAX(CASE WHEN Label='One' THEN D.Occurences END) as One
      ,MAX(CASE WHEN Label='One' THEN D.RangeBegin END) as OneRangeBegin
      ,MAX(CASE WHEN Label='One' THEN D.RangeEnd END) as OneRangeEnd
      ,MAX(CASE WHEN Label='One' THEN D.Unit END) as OneUnit
      ,MAX(CASE WHEN Label='Two' THEN D.Occurences END) as Two
      ,MAX(CASE WHEN Label='Two' THEN D.RangeBegin END) as TwoRangeBegin
      ,MAX(CASE WHEN Label='Two' THEN D.RangeEnd END) as vRangeEnd
      ,MAX(CASE WHEN Label='Two' THEN D.Unit END) as TwoUnit
    from
      AvailableData D
    group by
      D.ID, D.RangeBegin, D.RangeEnd