首先创建一个取消透视表的 CTE,以便每个代码位于单独的行上:
with cte(Name, IncidentId, CodeName, Code)
as(
select Name, IncidentId, CodeName, Code
from Incident i
unpivot(Code for CodeName in (Code1, Code2, Code3, Code4)) unpvt
)
现在您在 CTE 上对其自身进行外部联接,过滤掉排除的代码。这为每个 Name-Incident-Code 元组提供了一行,但在排除代码的行中有空值(您需要空行来维护正确的代码计数)。
Select *, t1.Name, t1.IncidentId, isnull(t2.Code, '') Code,
ROW_NUMBER() over(partition by t1.Name, t1.IncidentId order by isnull(t2.CodeName, 'zzz')) CodeNumber
from cte t1
left outer join cte t2 on t1.Name = t2.Name and
t1.IncidentId = t2.IncidentId and
t1.Code = t2.Code and
not exists(select 1 from Exclude e where e.Code = t2.Code)
此处的 ROW_NUMBER() 将创建新的 CodeNumber。 order byisnull(t2.CodeNumber, 'zzz')) 将空行推到末尾,以便首先对具有有效代码的行进行编号(因为“zzz”大于“Code-whatever-”)。
现在您只需将先前的查询转回,以便代码再次成为列:
select Name, IncidentId, [1] Code1, [2] Code2, [3] as Code3, [4] as Code4
from
(
Select t1.Name, t1.IncidentId, isnull(t2.Code, '') Code, ROW_NUMBER() over(partition by t1.Name, t1.IncidentId order by isnull(t2.CodeName, 'zzz')) CodeNumber
from cte t1
left outer join cte t2 on t1.Name = t2.Name and t1.IncidentId = t2.IncidentId and t1.Code = t2.Code and not exists(select 1 from Exclude e where e.Code = t2.Code)
) x
pivot(max(Code) for CodeNumber in ([1], [2], [3], [4])
) as pvt
SQL Fiddle
更新
上面的代码有几个问题。首先,当我使用 ROW_NUMBER() 创建 CodeNumber 时,我按 CodeName 排序。这在 9 个代码列之后会发生故障,因为它们不再正确排序(它们按字母顺序而不是数字顺序排序)。所以我需要在 CTE 中提取代码编号,以便稍后使用它进行排序:
with cte(Name, IncidentId, CodeName, CodeNumber, Code)
as(
select Name, IncidentId, CodeName, convert(int, SUBSTRING(CodeName, 5, len(CodeName))), Code
from Incident i
unpivot(Code for CodeName in (Code1, Code2, Code3, Code4, Code5, Code6, Code7, Code8, Code9, Code10)) unpvt
)
现在查询的其余部分如下所示:
select Name, IncidentId, [1] Code1, [2] Code2, [3] as Code3, [4] as Code4, [5] as Code5, [6] as Code6, [7] as Code7, [8] as Code8, [9] as Code9, [10] as Code10
from
(
Select t1.Name, t1.IncidentId, isnull(t2.Code, '') Code, ROW_NUMBER() over(partition by t1.Name, t1.IncidentId order by isnull(t2.CodeNumber, 999)) NewCodeNumber
from cte t1
left outer join cte t2 on t1.Name = t2.Name and t1.IncidentId = t2.IncidentId and t1.Code = t2.Code and not exists(select 1 from Exclude e where e.Code = t2.Code)
) x
pivot(max(Code) for NewCodeNumber in ([1], [2], [3], [4], [5], [6], [7], [8], [9], [10])
) as pvt
请注意,由于我现在在 CTE 中有一个名为 CodeNumber 的列,因此我将新生成的数字称为“NewCodeNumber”。另外,我通过t2.CodeNumber 订购,而不是t1.Code。
更新SQL Fiddle。
更新
关于您评论中的问题,您实质上是在询问是否取消旋转多个列,这不像取消旋转单个列那么简单。实现它的一种方法是分别取消透视代码和编码日期:
with cteCode(Name, IncidentId, CodeName, CodeNumber, Code)
as(
select Name, IncidentId, CodeName, convert(int, SUBSTRING(CodeName, 5, len(CodeName))), Code
from Incident i
unpivot(Code for CodeName in (Code1, Code2, Code3, Code4, Code5, Code6, Code7, Code8, Code9, Code10)) unpvt
), cteCodeDate(Name, IncidentId, CodeName, CodeNumber, CodeDate)
as(
select Name, IncidentId, CodeName, convert(int, SUBSTRING(CodeName, 9, len(CodeName))), CodeDate
from Incident i
unpivot(CodeDate for CodeName in (CodeDate1, CodeDate2, CodeDate3, CodeDate4, CodeDate5, CodeDate6, CodeDate7, CodeDate8, CodeDate9, CodeDate10)) unpvt
)
然后将它们重新组合在一起:
Select t1.Name, t1.IncidentId, isnull(t2.Code, '') Code, ROW_NUMBER() over(partition by t1.Name, t1.IncidentId order by isnull(t2.CodeNumber, 999)) NewCodeNumber, t3.CodeDate
from cteCode t1
join cteCodeDate t3 on t3.Name = t1.Name and t3.IncidentId = t1.IncidentId and t3.CodeNumber = t1.CodeNumber
left outer join cteCode t2 on t1.Name = t2.Name and t1.IncidentId = t2.IncidentId and t1.Code = t2.Code and not exists(select 1 from Exclude e where e.Code = t2.Code)
旋转多列也不像单列那么容易,所以我走了一条不同的路线来获得最终结果:
select Name, IncidentId,
MAX(case when newCodeNumber = 1 then Code end) Code1,
MAX(case when newCodeNumber = 1 then CodeDate end) CodeDate1,
MAX(case when newCodeNumber = 2 then Code end) Code2,
MAX(case when newCodeNumber = 2 then CodeDate end) CodeDate2,
MAX(case when newCodeNumber = 3 then Code end) Code3,
MAX(case when newCodeNumber = 3 then CodeDate end) CodeDate3,
MAX(case when newCodeNumber = 4 then Code end) Code4,
MAX(case when newCodeNumber = 4 then CodeDate end) CodeDate4,
MAX(case when newCodeNumber = 5 then Code end) Code5,
MAX(case when newCodeNumber = 5 then CodeDate end) CodeDate5,
MAX(case when newCodeNumber = 6 then Code end) Code6,
MAX(case when newCodeNumber = 6 then CodeDate end) CodeDate6,
MAX(case when newCodeNumber = 7 then Code end) Code7,
MAX(case when newCodeNumber = 7 then CodeDate end) CodeDate7,
MAX(case when newCodeNumber = 8 then Code end) Code8,
MAX(case when newCodeNumber = 8 then CodeDate end) CodeDate8,
MAX(case when newCodeNumber = 9 then Code end) Code9,
MAX(case when newCodeNumber = 9 then CodeDate end) CodeDate9,
MAX(case when newCodeNumber = 10 then Code end) Code10,
MAX(case when newCodeNumber = 10 then CodeDate end) CodeDate10
from
(
Select t1.Name, t1.IncidentId, isnull(t2.Code, '') Code, ROW_NUMBER() over(partition by t1.Name, t1.IncidentId order by isnull(t2.CodeNumber, 999)) NewCodeNumber, t3.CodeDate
from cteCode t1
join cteCodeDate t3 on t3.Name = t1.Name and t3.IncidentId = t1.IncidentId and t3.CodeNumber = t1.CodeNumber
left outer join cteCode t2 on t1.Name = t2.Name and t1.IncidentId = t2.IncidentId and t1.Code = t2.Code and not exists(select 1 from Exclude e where e.Code = t2.Code)
) x
group by Name, IncidentId
SQL Fiddle