没有聚合函数的动态 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 查看实际情况。
我有一个 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 查看实际情况。