没有聚合函数的动态 TSQL 数据透视表

Dynamic TSQL Pivot without aggregate function

我有一个 table 这样的 ('ExternalPersonRelationTable')

PersonId SubjectCode
4187 3
4187 278
4429 3
4429 4
4463 99
4464 174
4464 175

我想轮换数据,以便 table 中的每个人都得到一列和每个主题代码的 TRUE/FALSE 值,即 table 如下所示:

Code 4187 4429 4463 4464
3 TRUE TRUE FALSE FALSE
4 FALSE TRUE FALSE FALSE
...
99 FALSE FALSE TRUE FALSE
...
174 FALSE FALSE FALSE TRUE
175 FALSE FALSE FALSE TRUE

我想这是一个我应该使用 PIVOT 和动态解决的问题 SQL,但恐怕我的经验仅限于使用 CTE 和简单的 JOIN,所以我很难对数据进行 PIVOT ,没关系动态命名结果列。

SubjectCode 和 PersonId 最终会与其他 table 连接,以获取它们的真实值,但对于示例,我认为它们是不必要的。

我该如何解决这个问题?

示例数据

create table ExternalPersonRelationTable
(
  PersonId int,
  SubjectCode int
);

insert into ExternalPersonRelationTable (PersonId, SubjectCode) values
(4187, 3),
(4187, 278),
(4429, 3),
(4429, 4),
(4463, 99),
(4464, 174),
(4464, 175);

解决方案

从(有限的)静态版本的数据透视查询作为参考开始。

select piv.SubjectCode as Code,
       isnull(convert(bit, piv.[4187]), 0) as [4187],
       isnull(convert(bit, piv.[4429]), 0) as [4429],
       isnull(convert(bit, piv.[4463]), 0) as [4463],
       isnull(convert(bit, piv.[4464]), 0) as [4464]
from ExternalPersonRelationTable epr
pivot (max(epr.PersonId) for epr.PersonId in ([4187],[4429],[4463],[4464])) piv;

确定动态部分并构建(并验证)它们。

-- constuct lists
declare @fieldList nvarchar(1000);
declare @pivotList nvarchar(1000);

with cte as
(
  select epr.PersonId
  from ExternalPersonRelationTable epr
  group by epr.PersonId
)
select @fieldList = string_agg('isnull(convert(bit, piv.['
                             + convert(nvarchar(10), cte.PersonId)
                             + ']), 0) as ['
                             + convert(nvarchar(10), cte.PersonId)
                             + ']', ', ') within group (order by cte.PersonId),
       @pivotList = string_agg('['
                             + convert(nvarchar(10), cte.PersonId)
                             + ']', ',') within group (order by cte.PersonId)
from cte;

-- validate lists
select @fieldList as FieldList;
select @pivotList as PivotList;

在最终查询中合并动态部分(并在开发阶段验证)。

-- construct query
declare @query nvarchar(3000) = 'select piv.SubjectCode as Code, '
                              + @fieldList
                              + 'from ExternalPersonRelationTable epr '
                              + 'pivot (max(epr.PersonId) for epr.PersonId in ('
                              + @pivotList
                              + ')) piv;';
      
-- validate query
select @query as Query;

运行动态查询。

-- run query
exec sp_executesql @query;

结果

Code  4187   4429   4463   4464
----  -----  -----  -----  -----
  3   True   True   False  False
  4   False  True   False  False
 99   False  False  True   False
174   False  False  False  True
175   False  False  False  True
278   True   False  False  False

Fiddle 查看实际情况。