如何用递归查询构造SQL pivot table
how to construct SQL pivot table with recursive query
假设我有这些 tables
工单:
[Id], [ServiceId], [StatusId]
服务 <-- 自引用 table
[Id], [SName], [SCode], [SDnaCode], [SerivceParentId:Nullable]
状态
[Id], [StName], [StCode]
现在如何获得像这样的枢轴 table 结果:
|StName |RootService_1|RootService_2|...
+---------+-------------+-------------+----
|Initiated|23 |12 |...
|Closed |8 |5 |...
请记住,计数是针对根服务的,每个根服务都有自己的层次结构
[Service]
table
中的示例记录
|ID|SName |SCode|SDnaCode|SerivceParentId|
+--+-------------+-----+--------+---------------+
| 1|RootService_1|RS01 |RS01 |NULL |
| 2|RS01_Child_1 |C01 |RS01.C01|1 |
| 3|RS01_Child_2 |C02 |RS01.C02|1 |
| 4|RootService_2|RS02 |RS02 |NULL |
| 5|RS02_Child_1 |C01 |RS02.C01|2 |
| 6|RS02_Child_2 |C02 |RS02.C02|2 |
因此 RootService_1
下的示例主元 table 中的数字 23
是 Initiated WorkOrder
的计数 ServiceId
1,2 或3 合并
请记住,我想将其包装在动态 SQL 中以便为枢轴生成值 table for clause
.
我希望我能很好地解释我的问题,在此先感谢
我找到了一个解决方案,虽然我希望有一个文本数据依赖性较低的解决方案
此解决方案取决于 SDnaCode
列,在我的情况下是 ServiceDnaCode
,我将其设为存储过程
CREATE PROCEDURE DashboardGetServicesDataByStatus @lvl int, @parentId NVARCHAR(MAX)
AS
BEGIN
DECLARE
@columns NVARCHAR(MAX) = '',
@sql NVARCHAR(MAX) = '',
@sqlCommand nvarchar(max)='';
-- select the StatusName
set @sqlCommand = 'SELECT
@columns+=QUOTENAME(
(SELECT value from string_split(ServiceDnaCode,''.'')
order by CURRENT_TIMESTAMP
OFFSET @lvl rows
fetch next 1 rows only)) + '',''
FROM
[Service] where ParentServiceId' +
case
when @lvl>0 then ' IN(@parentId)'
else ' is null '
end
+ '
ORDER BY
Id;';
EXECUTE sp_executesql @sqlCommand, N'@lvl int,@parentId NVARCHAR(max),@columns NVARCHAR(max) OUTPUT'
, @lvl = @lvl,@parentId=@parentId, @columns=@columns OUTPUT
-- remove the last comma
SET @columns = LEFT(@columns, LEN(@columns) - 1);
print @columns
print @lvl
-- construct dynamic SQL
SET @sql ='
with cte_service as (
select * from [Service] where [Service].Id IN ('+CAST(@parentId as varchar(max))+')
union all
select e.* from [Service] e inner join cte_service o on e.ParentServiceId= o.Id
)
SELECT * FROM (
select count(wo.Id) ID,os.StatusName STATENAME ,
(SELECT value from string_split(ServiceDnaCode,''.'')
order by CURRENT_TIMESTAMP
OFFSET '+CAST(@lvl as varchar(max))+' rows
fetch next 1 rows only) SNAME from WorkOrder wo
left join RefWorkOrderStatus os on wo.OrderStatusCode=os.Id
left join [Service] s on wo.MainServiceId=s.Id
where wo.MainServiceId IN (select Id from cte_service)
group by os.StatusName,s.ServiceDnaCode
) t
pivot(
sum(ID)
for SNAME IN ('+ @columns +')
) as pivottable';
-- execute the dynamic SQL
EXECUTE sp_executesql @sql;
END
Go
假设我有这些 tables
工单:
[Id], [ServiceId], [StatusId]
服务 <-- 自引用 table
[Id], [SName], [SCode], [SDnaCode], [SerivceParentId:Nullable]
状态
[Id], [StName], [StCode]
现在如何获得像这样的枢轴 table 结果:
|StName |RootService_1|RootService_2|...
+---------+-------------+-------------+----
|Initiated|23 |12 |...
|Closed |8 |5 |...
请记住,计数是针对根服务的,每个根服务都有自己的层次结构
[Service]
table
|ID|SName |SCode|SDnaCode|SerivceParentId|
+--+-------------+-----+--------+---------------+
| 1|RootService_1|RS01 |RS01 |NULL |
| 2|RS01_Child_1 |C01 |RS01.C01|1 |
| 3|RS01_Child_2 |C02 |RS01.C02|1 |
| 4|RootService_2|RS02 |RS02 |NULL |
| 5|RS02_Child_1 |C01 |RS02.C01|2 |
| 6|RS02_Child_2 |C02 |RS02.C02|2 |
因此 RootService_1
下的示例主元 table 中的数字 23
是 Initiated WorkOrder
的计数 ServiceId
1,2 或3 合并
请记住,我想将其包装在动态 SQL 中以便为枢轴生成值 table for clause
.
我希望我能很好地解释我的问题,在此先感谢
我找到了一个解决方案,虽然我希望有一个文本数据依赖性较低的解决方案
此解决方案取决于 SDnaCode
列,在我的情况下是 ServiceDnaCode
,我将其设为存储过程
CREATE PROCEDURE DashboardGetServicesDataByStatus @lvl int, @parentId NVARCHAR(MAX)
AS
BEGIN
DECLARE
@columns NVARCHAR(MAX) = '',
@sql NVARCHAR(MAX) = '',
@sqlCommand nvarchar(max)='';
-- select the StatusName
set @sqlCommand = 'SELECT
@columns+=QUOTENAME(
(SELECT value from string_split(ServiceDnaCode,''.'')
order by CURRENT_TIMESTAMP
OFFSET @lvl rows
fetch next 1 rows only)) + '',''
FROM
[Service] where ParentServiceId' +
case
when @lvl>0 then ' IN(@parentId)'
else ' is null '
end
+ '
ORDER BY
Id;';
EXECUTE sp_executesql @sqlCommand, N'@lvl int,@parentId NVARCHAR(max),@columns NVARCHAR(max) OUTPUT'
, @lvl = @lvl,@parentId=@parentId, @columns=@columns OUTPUT
-- remove the last comma
SET @columns = LEFT(@columns, LEN(@columns) - 1);
print @columns
print @lvl
-- construct dynamic SQL
SET @sql ='
with cte_service as (
select * from [Service] where [Service].Id IN ('+CAST(@parentId as varchar(max))+')
union all
select e.* from [Service] e inner join cte_service o on e.ParentServiceId= o.Id
)
SELECT * FROM (
select count(wo.Id) ID,os.StatusName STATENAME ,
(SELECT value from string_split(ServiceDnaCode,''.'')
order by CURRENT_TIMESTAMP
OFFSET '+CAST(@lvl as varchar(max))+' rows
fetch next 1 rows only) SNAME from WorkOrder wo
left join RefWorkOrderStatus os on wo.OrderStatusCode=os.Id
left join [Service] s on wo.MainServiceId=s.Id
where wo.MainServiceId IN (select Id from cte_service)
group by os.StatusName,s.ServiceDnaCode
) t
pivot(
sum(ID)
for SNAME IN ('+ @columns +')
) as pivottable';
-- execute the dynamic SQL
EXECUTE sp_executesql @sql;
END
Go