7

私はここで何かが欠けていると確信しています。

私はこのようなデータセットを持っています:

FKRowNumber値タイプステータス
1 1 aaaaa A New
1 2bbbbbB良い
13ccccc悪い
1 4dddddC良い
1 5eeeeeB良い
2 1fffffC悪い
2 2 ggggg A New
2 3hhhhhC悪い
31iiiii良い
3 2jjjjjA良い

上位3つの結果をクエリし、それらを列としてピボットしたいので、最終結果セットは次のようになります。

FK Value1 Type1 Status1 Value2 Type2 Status2 Value3 Type3 Status3
1aaaaaA新しいbbbbbB良いcccccA悪い
2 fffff C Bad ggggg A New hhhhh C Bad
3iiiiiA良いjjjjjA良い

SQL Server 2005でこれを実現するにはどうすればよいですか?

私はPIVOTを使用してこれを試みてきましたが、まだそのキーワードに精通しておらず、希望どおりに機能させることができません。

SELECT * --Id, [1], [2], [3]
FROM
(
    SELECT Id, Value, Type, Status
    , ROW_NUMBER() OVER (PARTITION BY Id ORDER Status, Type) as [RowNumber]
    FROM MyTable
) as T
PIVOT
(
    -- I know this section doesn't work. I'm still trying to figure out PIVOT
    MAX(T.Value) FOR RowNumber IN ([1], [2], [3]),
    MAX(T.Type) FOR RowNumber IN ([1], [2], [3]),
    MAX(T.Status) FOR RowNumber IN ([1], [2], [3])
) AS PivotTable;

私の実際のデータセットはこれよりも少し複雑で、上位3つではなく、上位10のレコードが必要なので、それぞれに対して単純に実行したくありませんCASE WHEN RowNumber = X THEN...

アップデート

以下のすべての回答をテストしたところ、ほとんど同じように見え、小さなデータセット(約3kレコード)では明らかなパフォーマンスの違いはありませんでしたが、大きなデータセットに対してクエリを実行するとわずかな違いがありました。

これは、80,000レコードを使用し、上位10行の5列をクエリしたテストの結果です。したがって、最終結果セットは50列+Id列でした。自分でテストして、どちらが自分と環境に最適かを判断することをお勧めします。

  • データのピボット解除と再ピボットに関するbluefootの回答は、平均して約12秒で最速でした。読みやすく、保守しやすいので、この回答も気に入りました。

  • アーロンの答えコデロイドの答えはどちらも、を使用することを示唆しておりMAX(CASE WHEN RowNumber = X THEN ...)、平均して約13秒遅れていました。

  • 複数のステートメントを使用するというRodneyの回答PIVOTは、平均して約16秒でしたが、PIVOTステートメントが少ないほど高速になる可能性があります(私のテストでは5秒でした)。

  • そして、アーロンの回答の前半は、CTEの使用を提案しOUTER APPLY、最も遅かった。2分後にキャンセルしたため、実行にかかる時間はわかりません。これは、80kレコード、10行、5列ではなく、約3kレコード、3行、3列でした。

4

5 に答える 5

7

3つの別々のピボットステートメントでピボットを実行してみることができます。これを試してみてください:

SELECT Id
    ,MAX(S1) [Status 1]
    ,MAX(T1) [Type1]
    ,MAX(V1) [Value1]
    --, Add other columns
FROM
(
    SELECT Id, Value , Type, Status
    , 'S' + CAST(ROW_NUMBER() OVER (PARTITION BY Id ORDER BY Status, Type) AS VARCHAR(10)) [Status_RowNumber]
    , 'T' + CAST(ROW_NUMBER() OVER (PARTITION BY Id ORDER BY Status, Type) AS VARCHAR(10)) [Type_RowNumber]
    , 'V' + CAST(ROW_NUMBER() OVER (PARTITION BY Id ORDER BY Status, Type) AS VARCHAR(10)) [Value_RowNumber]
    FROM MyTable
) as T
PIVOT
(   
    MAX(Status) FOR Status_RowNumber IN ([S1], [S2], [S3],[S4],[S5],[S6],[S7],[S8],[S9],[S10])
)AS StatusPivot
PIVOT(
    MAX(Type) FOR Type_RowNumber IN ([T1], [T2], [T3],[T4],[T5],[T6],[T7],[T8],[T9],[T10])
)AS Type_Pivot
PIVOT(
    MAX(Value) FOR Value_RowNumber IN ([V1], [V2], [V3],[V4],[V5],[V6],[V7],[V8],[V9],[V10])
)AS Value_Pivot
GROUP BY Id

トップ10のレコードを選択するための基準の全範囲はわかりませんが、これにより、回答に近づく可能性のある出力が生成されます。

SQLフィドルの例

于 2012-08-31T19:32:09.657 に答える
7

UNPIVOTデータのを実行してから実行できPIVOTます。これは、静的または動的に実行できます。

静的バージョン:

select *
from
(
  select fk, col + cast(rownumber as varchar(1)) new_col,
    val
  from 
  (
    select fk, rownumber, value, cast(type as varchar(10)) type,
      status
    from yourtable
  ) x
  unpivot
  (
    val
    for col in (value, type, status)
  ) u
) x1
pivot
(
  max(val)
  for new_col in
    ([value1], [type1], [status1], 
     [value2], [type2], [status2],
    [value3], [type3])
) p

デモ付きのSQLフィドルを参照してください

動的バージョン。これにより、実行時に列のリストが取得されunpivot、次に取得されます。pivot

DECLARE @colsUnpivot AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX),
    @colsPivot as  NVARCHAR(MAX)

select @colsUnpivot = stuff((select ','+quotename(C.name)
         from sys.columns as C
         where C.object_id = object_id('yourtable') and
               C.name not in ('fk', 'rownumber')
         for xml path('')), 1, 1, '')

select @colsPivot = STUFF((SELECT  ',' 
                      + quotename(c.name 
                         + cast(t.rownumber as varchar(10)))
                    from yourtable t
                     cross apply 
                      sys.columns as C
                   where C.object_id = object_id('yourtable') and
                         C.name not in ('fk', 'rownumber')
                   group by c.name, t.rownumber
                   order by t.rownumber
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')


set @query 
  = 'select *
      from
      (
        select fk, col + cast(rownumber as varchar(10)) new_col,
          val
        from 
        (
          select fk, rownumber, value, cast(type as varchar(10)) type,
            status
          from yourtable
        ) x
        unpivot
        (
          val
          for col in ('+ @colsunpivot +')
        ) u
      ) x1
      pivot
      (
        max(val)
        for new_col in
          ('+ @colspivot +')
      ) p'

exec(@query)

SQL FiddlewithDemoを参照してください

どちらも同じ結果を生成しますが、事前に列の数がわからない場合は、動的に優れています。

動的バージョンは、行番号がすでにデータセットの一部であるという前提で機能しています。

于 2012-08-31T19:41:31.013 に答える
2

ロドニーのマルチピボットは賢いです、それは確かです。10Xと3Xの領域に入ると、もちろんあまり魅力的ではない他の2つの選択肢があります。

;WITH a AS
(
    SELECT Id, Value, Type, Status, 
      n = ROW_NUMBER() OVER (PARTITION BY Id ORDER BY [Status], [Type])
    FROM dbo.MyTable
)
SELECT a.Id, 
 Value1 = a.Value, Type1 = a.[Type], Status1 = a.[Status],
 Value2 = b.Value, Type2 = b.[Type], Status2 = b.[Status],
 Value3 = c.Value, Type3 = c.[Type], Status3 = c.[Status]
FROM a
OUTER APPLY (SELECT * FROM a AS T2 WHERE n = a.n + 1 AND id = a.id) AS b
OUTER APPLY (SELECT * FROM a AS T2 WHERE n = b.n + 1 AND id = b.id) AS c
WHERE a.n = 1
ORDER BY a.Id;

- また -

;WITH a AS
(
    SELECT Id, Value, [Type], [Status], 
      n = ROW_NUMBER() OVER (PARTITION BY Id ORDER BY [Status], [Type])
    FROM dbo.MyTable
)
SELECT Id,
  Value1  = MAX(CASE WHEN n = 1 THEN Value    END),
  Type1   = MAX(CASE WHEN n = 1 THEN [Type]   END),
  Status1 = MAX(CASE WHEN n = 1 THEN [Status] END),
  Value2  = MAX(CASE WHEN n = 2 THEN Value    END),
  Type2   = MAX(CASE WHEN n = 2 THEN [Type]   END),
  Status2 = MAX(CASE WHEN n = 2 THEN [Status] END),
  Value3  = MAX(CASE WHEN n = 3 THEN Value    END),
  Type3   = MAX(CASE WHEN n = 3 THEN [Type]   END),
  Status3 = MAX(CASE WHEN n = 3 THEN [Status] END)
FROM a
GROUP BY Id
ORDER BY a.Id;
于 2012-08-31T19:36:10.513 に答える
1

エレガントではありませんが、これでうまくいくかもしれません。

select aa.FK_Id
    , isnull(max(aa.Value1), '') as Value1
    , isnull(max(aa.Type1), '') as Type1
    , isnull(max(aa.Status1), '') as Status1
    , isnull(max(aa.Value2), '') as Value2
    , isnull(max(aa.Type2), '') as Type2
    , isnull(max(aa.Status2), '') as Status2
    , isnull(max(aa.Value3), '') as Value3
    , isnull(max(aa.Type3), '') as Type3
    , isnull(max(aa.Status3), '') as Status3
from
(       
    select FK_Id
            , case when RowNumber = 1 then Value else null end as Value1
            , case when RowNumber = 1 then [Type] else null end as Type1
            , case when RowNumber = 1 then [Status] else null end as Status1
            , case when RowNumber = 2 then Value else null end as Value2
            , case when RowNumber = 2 then [Type] else null end as Type2
            , case when RowNumber = 2 then [Status] else null end as Status2
            , case when RowNumber = 3 then Value else null end as Value3
            , case when RowNumber = 3 then [Type] else null end as Type3
            , case when RowNumber = 3 then [Status] else null end as Status3
    from Table1
) aa
group by aa.FK_Id
于 2012-08-31T19:44:12.963 に答える
1

次のようなものを試してください:

declare @rowCount int 
set @rowCount = 10

declare @isNullClause varchar(4024)
set @isnullClause = ''
declare @caseClause varchar(4024)
set @caseClause = ''

declare @i int 
set @i = 1

while(@i <= @rowCount) begin 
    set @isnullClause = @isNullClause + 
                        ' , max(aa.Value' + CAST(@i as varchar(3)) + ') as Value'    + CAST(@i as varchar(3)) +
                        ' , max(aa.Type' + CAST(@i as varchar(3)) + ') as Type'  + CAST(@i as varchar(3)) +
                        ' , max(aa.Status' + CAST(@i as varchar(3)) + ') as Status'  + CAST(@i as varchar(3)) + ' '; 
    set @caseClause = @caseClause + 
        ' , case when RowNumber = ' + CAST(@i as varchar(3)) + ' then Value else null end as Value' + CAST(@i as varchar(3)) +
        ' , case when RowNumber = ' + CAST(@i as varchar(3)) + ' then Type else null end as Type' + CAST(@i as varchar(3)) +
        ' , case when RowNumber = ' + CAST(@i as varchar(3)) + ' then Status else null end as Status' + CAST(@i as varchar(3)) + ' '


    set @i = @i + 1; 
end

declare @sql nvarchar(4000)
set @sql = 'select aa.FK_Id ' + @isnullClause + ' from ( select FK_Id ' 
            + @caseClause + '  from Table1) aa group by aa.FK_Id '

exec SP_EXECUTESQL @sql
于 2012-08-31T20:54:56.763 に答える