我有两张桌子" Controls"和" ControlChilds"
父表结构:
Create table Controls(
ProjectID Varchar(20) NOT NULL,
ControlID INT NOT NULL,
ControlCode Varchar(2) NOT NULL,
ControlPoint Decimal NULL,
ControlScore Decimal NULL,
ControlValue Varchar(50)
)
样本数据
ProjectID | ControlID | ControlCode | ControlPoint | ControlScore | ControlValue
P001 1 A 30.44 65 Invalid
P001 2 C 45.30 85 Valid
子表结构:
Create table ControlChilds(
ControlID INT NOT NULL,
ControlChildID INT NOT NULL,
ControlChildValue Varchar(200) NULL
)
样本数据
ControlID | ControlChildID | ControlChildValue
1 100 Yes
1 101 No
1 102 NA
1 103 Others
2 104 Yes
2 105 SomeValue
对于给定的ProjectID,输出应该在一行中,其所有的Control值都是&其次是子控件值(基于ControlCode(即)ControlCode_Child(1,2,3 ......),它应该看起来像这样
此外,我尝试了这个PIVOT查询,我能够获取ChildControls表值,但我不知道如何获取Controls表值。
DECLARE @cols AS NVARCHAR(MAX);
DECLARE @query AS NVARCHAR(MAX);
select @cols = STUFF((SELECT
distinct ',' +
QUOTENAME(ControlCode + '_Child' + CAST(ROW_NUMBER() over(PARTITION BY ControlCode ORDER BY ControlChildID) AS Varchar(25)))
FROM Controls C
INNER JOIN ControlChilds CC
ON C.ControlID = CC.ControlID
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '');
SELECT @query ='SELECT *
FROM
(
SELECT
(ControlCode + ''_Child'' + CAST(ROW_NUMBER() over(PARTITION BY ControlCode ORDER BY ControlChildID) AS Varchar(25))) As Code,
ControlChildValue
FROM Controls AS C
INNER JOIN ControlChilds AS CC ON C.ControlID = CC.ControlID
) AS t
PIVOT
(
MAX(ControlChildValue)
FOR Code IN( ' + @cols + ' )' +
' ) AS p ; ';
execute(@query);
我得到的输出:
有谁可以帮我解决如何在每个ControlChilds表值前面获取Controls表值?
答案 0 :(得分:6)
你在这里有点乱,因为你有两个不同结构的表,你想要转动多个列。我首先开始编写查询的静态版本以使逻辑正确,然后完成编写动态版本的过程。
由于您想要转动多个列,因此您需要首先取消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 with Demo。这会将您的多行转换为多个列,类似于:
| 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 with Demo。这将从以下格式获取此表中的数据:
| 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
并应用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版本:
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 :(得分:1)
要PIVOT
您的数据首先需要UNPIVOT
部分数据,Controls
表中的部分。
准备查询成为
SELECT ProjectID, ControlCode + '_' + [Field] [Field], Value
FROM (SELECT ProjectID
, ControlCode
, CAST(ControlPoint AS SQL_Variant) ControlPoint
, CAST(ControlScore AS SQL_Variant) ControlScore
, CAST(ControlValue AS SQL_Variant) ControlValue
FROM Controls C) D
UNPIVOT
(Value FOR [Field] IN (ControlPoint, ControlScore, ControlValue)) p
UNION ALL
SELECT ProjectID, ControlCode + '_Child' + RowID [Field], Value
FROM (SELECT C.ProjectID
, C.ControlCode
, RowID = CAST(ROW_NUMBER()
OVER (PARTITION BY CC.ControlID
ORDER BY CC.ControlChildID) AS VARCHAR)
, CAST(CC.ControlChildValue AS SQL_Variant) ControlChildValue
FROM Controls C
INNER JOIN ControlChilds CC ON C.ControlID = CC.ControlID) D
UNPIVOT
(Value FOR [Field] IN (ControlChildValue)) p
以格式
获取数据PROJECTID | FIELD | VALUE
----------+----------------+----------
P001 | A_ControlPoint | 30
P001 | A_ControlScore | 65
P001 | A_ControlValue | Invalid
P001 | C_ControlPoint | 45
P001 | C_ControlScore | 85
P001 | C_ControlValue | Valid
P001 | A_Child1 | Yes
P001 | A_Child2 | No
P001 | A_Child3 | NA
P001 | A_Child4 | Others
P001 | C_Child1 | Yes
P001 | C_Child2 | SomeValue
现在可以进入PIVOT
DECLARE @query AS NVARCHAR(MAX)
DECLARE @cols AS NVARCHAR(MAX)
WITH Q AS (
SELECT ProjectID, ControlCode + '_' + [Field] [Field], Value
, ControlCode
, ID = CASE WHEN [Field] = 'ControlPoint' THEN 1
WHEN [Field] = 'ControlScore' THEN 2
WHEN [Field] = 'ControlValue' THEN 3
END
FROM (SELECT ProjectID
, ControlCode
, CAST(ControlPoint AS SQL_Variant) ControlPoint
, CAST(ControlScore AS SQL_Variant) ControlScore
, CAST(ControlValue AS SQL_Variant) ControlValue
FROM Controls C) D
UNPIVOT
(Value FOR [Field] IN (ControlPoint, ControlScore, ControlValue)) p
UNION ALL
SELECT ProjectID, ControlCode + '_Child' + RowID [Field], Value
, ControlCode
, ID = RowID + 3
FROM (SELECT C.ProjectID
, C.ControlCode
, RowID = CAST(ROW_NUMBER() OVER (PARTITION BY CC.ControlID ORDER BY CC.ControlChildID) AS VARCHAR)
, CAST(CC.ControlChildValue AS SQL_Variant) ControlChildValue
FROM Controls C
INNER JOIN ControlChilds CC ON C.ControlID = CC.ControlID) D
UNPIVOT
(Value FOR [Field] IN (ControlChildValue)) p
)
SELECT @cols = STUFF((SELECT ',' + QUOTENAME([Field])
FROM Q
ORDER BY ControlCode, ID
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
, 1, 1, '')
SELECT @query ='
WITH Q AS (
SELECT ProjectID, ControlCode + ''_'' + [Field] [Field], Value
FROM (SELECT ProjectID
, ControlCode
, CAST(ControlPoint AS SQL_Variant) ControlPoint
, CAST(ControlScore AS SQL_Variant) ControlScore
, CAST(ControlValue AS SQL_Variant) ControlValue
FROM Controls C) D
UNPIVOT
(Value FOR [Field] IN (ControlPoint, ControlScore, ControlValue)) p
UNION ALL
SELECT ProjectID, ControlCode + ''_Child'' + RowID [Field], Value
FROM (SELECT C.ProjectID
, C.ControlCode
, RowID = CAST(ROW_NUMBER() OVER (PARTITION BY CC.ControlID ORDER BY CC.ControlChildID) AS VARCHAR)
, CAST(CC.ControlChildValue AS SQL_Variant) ControlChildValue
FROM Controls C
INNER JOIN ControlChilds CC ON C.ControlID = CC.ControlID) D
UNPIVOT
(Value FOR [Field] IN (ControlChildValue)) p
)
SELECT *
FROM (SELECT ProjectID, [Field], Value FROM Q) AS t
PIVOT
(MAX(Value) FOR [Field] IN( ' + @cols + ' )) AS p ;'
execute(@query);
要按所需顺序获取字段,必须在列字符串中强制执行特定顺序,因此在SELECT ... FOR XML
中,由于没有可用的订单列,因此需要创建新订单,即ID
CTE