Sqlserver
 sql >> Teknologi Basis Data >  >> RDS >> Sqlserver

SQL Server - kolom Pivot Dinamis Kompleks

Anda memiliki sedikit kekacauan di sini karena Anda memiliki dua tabel dalam struktur yang berbeda dan Anda ingin memutar beberapa kolom. Pertama-tama saya akan mulai menulis versi statis kueri Anda untuk mendapatkan logika yang benar, kemudian melalui proses penulisan versi dinamis.

Karena Anda ingin memutar beberapa kolom, Anda harus melepas beberapa kolom di Controls tabel terlebih dahulu, lalu pivot. Anda telah menandai ini sebagai SQL Server 2008, sehingga Anda dapat menggunakan CROSS APPLY untuk melepaskan kolom.

Saya sarankan mengambil langkah-langkah berikut. Pertama, lepaskan controls tabel:

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)

Lihat SQL Fiddle dengan Demo . Ini akan mengubah beberapa baris Anda menjadi beberapa kolom yang mirip dengan:

| 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 |

Kedua, dapatkan data dari ControlChilds tabel ke dalam format yang serupa tetapi gunakan row_number() . Anda untuk menetapkan urutan untuk setiap anak:

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

Lihat SQL Fiddle dengan Demo . Ini mendapatkan data dari tabel ini dalam format:

| 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 |

Sekarang, Anda dapat dengan mudah menggunakan UNION ALL antara dua kueri dan terapkan fungsi 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;

Lihat SQL Fiddle dengan Demo .

Sekarang setelah Anda memiliki logika yang benar, Anda dapat mengonversinya ke versi SQL dinamis:

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;

Lihat SQL Fiddle dengan Demo . Saya menulis versi dinamis untuk menjaga kolom dalam urutan yang Anda gunakan dalam sampel Anda. Ini dapat dilakukan dengan menggunakan jenis urutan nilai.

Ini memberikan hasil akhir dari:

| 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. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. nodejs mssql masukkan dengan parameter

  2. Cara cepat menulis Select Query di SQL Server - Tutorial SQL Server / TSQL Bagian 108

  3. Threading di CLR di SQL Server 2008

  4. Cara memilih baris maksimal untuk setiap grup dalam SQL

  5. Bagaimana cara menjatuhkan batasan kunci asing hanya jika ada di server sql?