SQL Server 2017及以上版本
STRING_AGG
确实,STRING_AGG
是 SQL Server 2017 (版本 14.x) 及更高版本中引入的一个聚合函数,它允许你将多个行的值组合成一个字符串,并且你可以指定一个分隔符来分隔这些值。由于你正在使用 SQL Server 2014,STRING_AGG
函数在你的环境中是不可用的。
在 SQL Server 2014 中,要达到类似 STRING_AGG
的效果,你可以使用 FOR XML PATH
方法,这是 SQL Server 较早版本中常用的技巧来聚合字符串。下面是一个使用 FOR XML PATH
来模拟 STRING_AGG
功能的示例:
DECLARE @cols AS NVARCHAR(MAX);
SELECT @cols = STUFF((
SELECT ',' + QUOTENAME(FNUMBER)
FROM (
SELECT DISTINCT FNUMBER
FROM YourTable -- 替换为你的实际表名
) AS SubQuery
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, ''); -- 移除第一个逗号
-- 现在 @cols 包含了用逗号分隔的、带引号的 FNUMBER 列表
在这个示例中,STUFF
函数用于移除由 FOR XML PATH
生成的字符串列表的第一个逗号。QUOTENAME
函数用于确保列名被正确地用方括号括起来,这在处理包含特殊字符或保留字的列名时特别有用。
一旦你有了这个列表,你就可以像之前的示例那样构建你的动态 PIVOT 查询。
示例
DECLARE @cols AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX);
-- 获取所有唯一的FNUMBER值,并构建用于PIVOT操作的列列表
SELECT @cols = STRING_AGG(QUOTENAME(FNUMBER), ',')
FROM (
SELECT DISTINCT r1.FNUMBER
FROM t_sec_FuncPermission p1
INNER JOIN t_sec_funcPermissionEntry p2 ON p1.FItemID = p2.FItemID
INNER JOIN T_SEC_PERMISSIONITEM p3 ON p2.FPermissionItemID = p3.FItemID
INNER JOIN t_meta_objectType o1 ON p1.FObjectTypeID = o1.FID AND o1.fdevtype != 2
INNER JOIN t_sec_role r1 ON p1.FRoleID = r1.FRoleID
Where 1=1
) AS RoleNumbers;
-- 构建动态PIVOT查询
SET @query = '
SELECT FObjectTypeID, FItemID, ' + @cols + '
FROM
(
SELECT r1.FNUMBER,
p1.FObjectTypeID,
p3.FItemID,
CASE p2.FPERMISSIONSTATUS
WHEN ''0'' THEN ''有权''
WHEN ''2'' THEN ''禁止''
ELSE ''无权''
END AS FPermissionStatusName
FROM
t_sec_FuncPermission p1
INNER JOIN t_sec_funcPermissionEntry p2 ON p1.FItemID = p2.FItemID
INNER JOIN T_SEC_PERMISSIONITEM p3 ON p2.FPermissionItemID = p3.FItemID
INNER JOIN t_meta_objectType o1 ON p1.FObjectTypeID = o1.FID AND o1.fdevtype != 2
INNER JOIN t_sec_role r1 ON p1.FRoleID = r1.FRoleID
Where 1=1
) x
PIVOT
(
MAX(FPermissionStatusName) FOR FNUMBER IN (' + @cols + ')
) p ';
-- 执行动态查询
EXEC sp_executesql @query;
SQL Server 2014以下版本
在 SQL Server 2014 中,STRING_AGG
函数是不可用的,因为这是一个在 SQL Server 2017(版本 14.x)及更高版本中引入的聚合函数。为了在 SQL Server 2014 中实现类似的功能,你可以使用 FOR XML PATH
方法来生成一个由逗号分隔的字符串列表。
以下是将你的查询修改为适用于 SQL Server 2014 的版本:
DECLARE @cols AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX);
-- 获取所有唯一的FNUMBER值,并构建用于PIVOT操作的列列表
SELECT @cols = STUFF((
SELECT ',' + QUOTENAME(FNUMBER)
FROM (
SELECT DISTINCT r1.FNUMBER
FROM t_sec_FuncPermission p1
INNER JOIN t_sec_funcPermissionEntry p2 ON p1.FItemID = p2.FItemID
INNER JOIN T_SEC_PERMISSIONITEM p3 ON p2.FPermissionItemID = p3.FItemID
INNER JOIN t_meta_objectType o1 ON p1.FObjectTypeID = o1.FID AND o1.fdevtype != 2
INNER JOIN t_sec_role r1 ON p1.FRoleID = r1.FRoleID
-- Where 1=1 这个条件实际上没有作用,可以移除
) AS RoleNumbers
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)'), 1, 1, ''); -- 使用STUFF函数移除最前面的逗号
-- 构建动态PIVOT查询
SET @query = '
SELECT FObjectTypeID, FItemID, ' + @cols + '
FROM (
SELECT r1.FNUMBER,
p1.FObjectTypeID,
p3.FItemID,
CASE p2.FPERMISSIONSTATUS
WHEN ''0'' THEN ''有权''
WHEN ''2'' THEN ''禁止''
ELSE ''无权''
END AS FPermissionStatusName
FROM t_sec_FuncPermission p1
INNER JOIN t_sec_funcPermissionEntry p2 ON p1.FItemID = p2.FItemID
INNER JOIN T_SEC_PERMISSIONITEM p3 ON p2.FPermissionItemID = p3.FItemID
INNER JOIN t_meta_objectType o1 ON p1.FObjectTypeID = o1.FID AND o1.fdevtype != 2
INNER JOIN t_sec_role r1 ON p1.FRoleID = r1.FRoleID
-- Where 1=1 这个条件实际上没有作用,可以移除
) x
PIVOT (
MAX(FPermissionStatusName) FOR FNUMBER IN (' + @cols + ')
) p';
-- 执行动态查询
EXEC sp_executesql @query;
在这个修改后的版本中,我使用了 FOR XML PATH
来生成一个 XML 字符串,然后通过 .value('.', 'NVARCHAR(MAX)')
方法将其转换为 NVARCHAR(MAX)
类型。接着,我使用 STUFF
函数来移除字符串最前面的逗号,从而得到一个由逗号分隔的列名列表。
请注意,我在查询中移除了 Where 1=1
条件,因为它实际上并不对查询结果产生任何影响。如果你需要添加特定的过滤条件,你应该在 INNER JOIN
子句之前或之后添加它们。