sql >> データベース >  >> RDS >> Sqlserver

SQL Server - 複雑な動的ピボット列

    異なる構造の 2 つのテーブルがあり、複数の列をピボットしたいので、ここでは少し混乱しています。最初にクエリの静的バージョンを作成してロジックを正しくし、次に動的バージョンを作成するプロセスを実行します。

    複数の列をピボットしたいので、Controls でいくつかの列のピボットを解除する必要があります 最初にテーブル、次にピボット。これを SQL Server 2008 としてタグ付けしたので、CROSS APPLY を使用できます 列のピボットを解除します。

    次の手順を実行することをお勧めします。まず、controls のピボットを解除します テーブル:

    select 
      ProjectId,
      col = ControlCode +'_'+col,
      val
    from
    (
      select 
        c.ProjectId,
        c.ControlCode,
        c.ControlPoint,
        c.ControlScore,
        c.ControlValue
      from controls c
    ) d
    cross apply
    (
      select 'ControlPoint', cast(controlpoint as varchar(10)) union all
      select 'ControlScore', cast(ControlScore as varchar(10)) union all
      select 'ControlValue', ControlValue
    ) c (col, val)
     

    デモによる SQL Fiddle を参照してください。 .これにより、次のように複数の行が複数の列に変換されます:

    <プレ>| PROJECTID | COL | VAL | |-----------|----------------|---------| | P001 | A_ControlPoint | 30.44 | | P001 | A_ControlScore | 65.00 | | P001 | A_ControlValue | Invalid | | P001 | C_ControlPoint | 45.30 | | P001 | C_ControlScore | 85.00 | | P001 | C_ControlValue | Valid |

    次に、ControlChilds からデータを取得します テーブルを同様の形式に変換しますが、 row_number() を使用してください 各子にシーケンスを割り当てる:

    select 
      projectId,
      col = ControlCode+'_'+'Child'+cast(seq as varchar(10)),
      ControlChildValue
    from
    (
      select c.ProjectId,
        c.ControlCode,
        cc.ControlChildValue,
        row_number() over(partition by c.ProjectId, c.ControlCode
                          order by cc.ControlChildId) seq
      from controls c
      inner join controlchilds cc
        on c.controlid = cc.controlid
    ) d
     

    デモによる SQL Fiddle を参照してください。 .これは、このテーブルから次の形式でデータを取得します:

    <プレ>| PROJECTID | COL | CONTROLCHILDVALUE | |-----------|----------|-------------------| | P001 | A_Child1 | Yes | | P001 | A_Child2 | No | | P001 | A_Child3 | NA | | P001 | A_Child4 | Others | | P001 | C_Child1 | Yes | | P001 | C_Child2 | SomeValue |

    これで、UNION ALL を簡単に使用できます 2 つのクエリの間に PIVOT 関数を適用します。

    select ProjectId,
      A_ControlPoint, A_ControlScore, A_ControlValue,
      A_Child1, A_Child2, A_Child3, A_Child4,
      C_ControlPoint, C_ControlScore, C_ControlValue,
      C_Child1, C_Child2
    from
    (
      select 
        ProjectId,
        col = ControlCode +'_'+col,
        val
      from
      (
        select 
          c.ProjectId,
          c.ControlCode,
          c.ControlPoint,
          c.ControlScore,
          c.ControlValue
        from controls c
      ) d
      cross apply
      (
        select 'ControlPoint', cast(controlpoint as varchar(10)) union all
        select 'ControlScore', cast(ControlScore as varchar(10)) union all
        select 'ControlValue', ControlValue
      ) c (col, val)
      union all
      select 
        projectId,
        col = ControlCode+'_'+'Child'+cast(seq as varchar(10)),
        ControlChildValue
      from
      (
        select c.ProjectId,
          c.ControlCode,
          cc.ControlChildValue,
          row_number() over(partition by c.ProjectId, c.ControlCode
                            order by cc.ControlChildId) seq
        from controls c
        inner join controlchilds cc
          on c.controlid = cc.controlid
      ) d
    ) src
    pivot
    (
      max(val)
      for col in (A_ControlPoint, A_ControlScore, A_ControlValue,
                  A_Child1, A_Child2, A_Child3, A_Child4,
                  C_ControlPoint, C_ControlScore, C_ControlValue,
                  C_Child1, C_Child2)
    ) piv;
     

    デモによる SQL Fiddle を参照してください。 .

    正しいロジックが得られたので、これを動的 SQL バージョンに変換できます。

    DECLARE @cols AS NVARCHAR(MAX),
        @query  AS NVARCHAR(MAX)
    
    select @cols = STUFF((SELECT ',' + QUOTENAME(col) 
                        from 
                        (
                          select ControlCode,
                            col = ControlCode +'_'+col,
                            seq, 
                            so
                          from controls
                          cross apply
                          (
                            select 'ControlPoint', 0, 0 union all
                            select 'ControlScore', 0, 1 union all
                            select 'ControlValue', 0, 2 
                          ) c (col, seq, so)
                          union all
                          select  ControlCode,
                            col = ControlCode+'_'+'Child'+cast(seq as varchar(10)),
                            seq, 
                            3
                          from
                          (
                            select ControlCode, 
                              row_number() over(partition by c.ProjectId, c.ControlCode
                                                      order by cc.ControlChildId) seq
                            from controls c
                            inner join controlchilds cc
                              on c.controlid = cc.controlid
                          ) d
                        ) src
                        group by ControlCode, seq, col, so
                        order by ControlCode, so, seq
                FOR XML PATH(''), TYPE
                ).value('.', 'NVARCHAR(MAX)') 
            ,1,1,'')
    
    
    set @query = 'SELECT ProjectId, ' + @cols + ' 
                from 
                (
                  select ProjectId,
                    col = ControlCode +''_''+col,
                    val
                  from
                  (
                    select 
                      c.ProjectId,
                      c.ControlCode,
                      c.ControlPoint,
                      c.ControlScore,
                      c.ControlValue
                    from controls c
                  ) d
                  cross apply
                  (
                    select ''ControlPoint'', cast(controlpoint as varchar(10)) union all
                    select ''ControlScore'', cast(ControlScore as varchar(10)) union all
                    select ''ControlValue'', ControlValue
                  ) c (col, val)
                  union all
                  select 
                    projectId,
                    col = ControlCode+''_Child''+cast(seq as varchar(10)),
                    ControlChildValue
                  from
                  (
                    select c.ProjectId,
                      c.ControlCode,
                      cc.ControlChildValue,
                      row_number() over(partition by c.ProjectId, c.ControlCode
                                        order by cc.ControlChildId) seq
                    from controls c
                    inner join controlchilds cc
                      on c.controlid = cc.controlid
                  ) d
                ) x
                pivot 
                (
                    max(val)
                    for col in (' + @cols + ')
                ) p '
    
    exec sp_executesql @query;
     

    SQL Fiddle with Demo を参照してください。 .サンプルで使用した順序で列を維持するために、動的バージョンを作成しました。これは、ソート順タイプの値を使用して実行できます。

    これにより、次の最終結果が得られます:

    <プレ>| PROJECTID | A_CONTROLPOINT | A_CONTROLSCORE | A_CONTROLVALUE | A_CHILD1 | A_CHILD2 | A_CHILD3 | A_CHILD4 | C_CONTROLPOINT | C_CONTROLSCORE | C_CONTROLVALUE | C_CHILD1 | C_CHILD2 | |-----------|----------------|----------------|----------------|----------|----------|----------|----------|----------------|----------------|----------------|----------|-----------| | P001 | 30.44 | 65.00 | Invalid | Yes | No | NA | Others | 45.30 | 85.00 | Valid | Yes | SomeValue |

    1. 64ビットアプリケーションをClarionTopSpeedに接続する

    2. JPAを介して行をランダムに選択

    3. mysqlDATETIME列をエポック秒に変換します

    4. PL/pgSQL関数のintリテラルとintパラメータの違いを理解する