Vous avez un peu de désordre ici parce que vous avez deux tables dans des structures différentes et que vous voulez faire pivoter plusieurs colonnes. Je commencerais par écrire une version statique de votre requête pour obtenir la logique correcte, puis je passerais par le processus d'écriture d'une version dynamique.
Puisque vous voulez faire pivoter plusieurs colonnes, vous devrez défaire pivoter plusieurs colonnes dans les Controls
tableau d'abord, puis pivotez. Vous avez marqué ceci comme SQL Server 2008, vous pouvez donc utiliser CROSS APPLY
pour ne pas pivoter les colonnes.
Je suggérerais de suivre les étapes suivantes. Tout d'abord, dépivotez les controls
tableau :
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)
Voir SQL Fiddle avec démo . Cela va convertir vos multiples lignes en plusieurs colonnes similaires à :
| 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 |
Deuxièmement, récupérez les données de ControlChilds
table dans un format similaire mais utilisez votre row_number()
pour attribuer une séquence à chaque enfant :
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
Voir SQL Fiddle avec démo . Cela obtient les données de cette table au 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 |
Maintenant, vous pouvez facilement utiliser UNION ALL
entre les deux requêtes et appliquer la fonction 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;
Voir SQL Fiddle avec démo .
Maintenant que vous avez la bonne logique, vous pouvez la convertir en une version SQL dynamique :
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;
Voir SQL Fiddle avec démo . J'ai écrit la version dynamique pour conserver les colonnes dans l'ordre que vous avez utilisé dans votre exemple. Cela peut être fait en utilisant un type d'ordre de tri de valeur.
Cela donne un résultat final de :
| 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 |