简化使用 FOR XML 连接列值的 SQL 视图语句

Simplify SQL view statement that concatenates column values with FOR XML

我已经为视图创建了一个语句,但我对它不满意,因为它多次包含相同的 SELECT。到目前为止,我无法调整声明以大大缩短它。也许你可以给我一个提示或告诉我我可以从哪里开始简化这个:

SELECT
    STUFF((SELECT ', ' + t2.ToolId
        FROM (
            SELECT CONVERT(NVARCHAR(36), Tool.Id) AS ToolId, Tool.Name AS ToolName, TGroup.Id AS GroupId, TGroup.Name AS GroupName, Base.Id,
                Base.Position, Task.Definition, Task.IsEvaluationActive
            FROM dbo.CustomTask AS Task INNER JOIN
            dbo.TaskBase AS Base ON Task.Id = Base.Id LEFT OUTER JOIN
            dbo.TaskGroup AS TGroup ON Base.TaskGroupId = TGroup.Id LEFT OUTER JOIN
            dbo.ToolTaskGroupMapping AS Map ON TGroup.Id = Map.TaskGroupId LEFT OUTER JOIN
            dbo.Tool AS Tool ON Map.ToolId = Tool.Id) t2
        WHERE t1.GroupId = t2.GroupId AND t1.Id = t2.Id
        ORDER BY t2.ToolId
        FOR XML PATH(''), TYPE ).value('.', 'nvarchar(max)'), 1, 2, '') AS ToolId,
    STUFF((SELECT ', ' + t2.ToolName
        FROM (
            SELECT CONVERT(NVARCHAR(36), Tool.Id) AS ToolId, Tool.Name AS ToolName, TGroup.Id AS GroupId, TGroup.Name AS GroupName, Base.Id,
                Base.Position, Task.Definition, Task.IsEvaluationActive
            FROM dbo.CustomTask AS Task INNER JOIN
            dbo.TaskBase AS Base ON Task.Id = Base.Id LEFT OUTER JOIN
            dbo.TaskGroup AS TGroup ON Base.TaskGroupId = TGroup.Id LEFT OUTER JOIN
            dbo.ToolTaskGroupMapping AS Map ON TGroup.Id = Map.TaskGroupId LEFT OUTER JOIN
            dbo.Tool AS Tool ON Map.ToolId = Tool.Id) t2
        WHERE t1.GroupId = t2.GroupId AND t1.Id = t2.Id
        ORDER BY t2.ToolId
        FOR XML PATH(''), TYPE ).value('.', 'nvarchar(max)'), 1, 2, '') AS ToolName, 
    t1.GroupId, t1.GroupName, t1.Id, t1.Position, t1.Definition, t1.IsEvaluationActive
FROM (
    SELECT CONVERT(NVARCHAR(36), Tool.Id) AS ToolId, Tool.Name AS ToolName, TGroup.Id AS GroupId, TGroup.Name AS GroupName, Base.Id,
        Base.Position, Task.Definition, Task.IsEvaluationActive
    FROM dbo.CustomTask AS Task INNER JOIN
    dbo.TaskBase AS Base ON Task.Id = Base.Id LEFT OUTER JOIN
    dbo.TaskGroup AS TGroup ON Base.TaskGroupId = TGroup.Id LEFT OUTER JOIN
    dbo.ToolTaskGroupMapping AS Map ON TGroup.Id = Map.TaskGroupId LEFT OUTER JOIN
    dbo.Tool AS Tool ON Map.ToolId = Tool.Id) t1
GROUP BY t1.GroupId, t1.GroupName, t1.Id, t1.Position, t1.Definition, t1.IsEvaluationActive

有没有办法避免重复以下部分?

SELECT CONVERT(NVARCHAR(36), Tool.Id) AS ToolId, Tool.Name AS ToolName, TGroup.Id AS GroupId, TGroup.Name AS GroupName, Base.Id,
    Base.Position, Task.Definition, Task.IsEvaluationActive
FROM dbo.CustomTask AS Task INNER JOIN
dbo.TaskBase AS Base ON Task.Id = Base.Id LEFT OUTER JOIN
dbo.TaskGroup AS TGroup ON Base.TaskGroupId = TGroup.Id LEFT OUTER JOIN
dbo.ToolTaskGroupMapping AS Map ON TGroup.Id = Map.TaskGroupId LEFT OUTER JOIN
dbo.Tool AS Tool ON Map.ToolId = Tool.Id

您可以使用 WITH 子句:

;with example as (
    SELECT CONVERT(NVARCHAR(36), Tool.Id) AS ToolId, Tool.Name AS ToolName, TGroup.Id AS GroupId, TGroup.Name AS GroupName, Base.Id,
        Base.Position, Task.Definition, Task.IsEvaluationActive
    FROM dbo.CustomTask AS Task INNER JOIN
    dbo.TaskBase AS Base ON Task.Id = Base.Id LEFT OUTER JOIN
    dbo.TaskGroup AS TGroup ON Base.TaskGroupId = TGroup.Id LEFT OUTER JOIN
    dbo.ToolTaskGroupMapping AS Map ON TGroup.Id = Map.TaskGroupId LEFT OUTER JOIN
    dbo.Tool AS Tool ON Map.ToolId = Tool.Id
)
SELECT
    STUFF((SELECT ', ' + t2.ToolId
        FROM example t2
        WHERE t1.GroupId = t2.GroupId AND t1.Id = t2.Id
        ORDER BY t2.ToolId
        FOR XML PATH(''), TYPE ).value('.', 'nvarchar(max)'), 1, 2, '') AS ToolId,
    STUFF((SELECT ', ' + t2.ToolName
        FROM example t2
        WHERE t1.GroupId = t2.GroupId AND t1.Id = t2.Id
        ORDER BY t2.ToolId
        FOR XML PATH(''), TYPE ).value('.', 'nvarchar(max)'), 1, 2, '') AS ToolName, 
    t1.GroupId, t1.GroupName, t1.Id, t1.Position, t1.Definition, t1.IsEvaluationActive
FROM example t1
GROUP BY t1.GroupId, t1.GroupName, t1.Id, t1.Position, t1.Definition, t1.IsEvaluationActive

SQL 引入了 Server 2016 STRING_AGG。假设原始查询中的所有子查询都相同,则 SQL Server 2016 中的等效项可能是:

select 
    STRING_AGG(Tool.ID,',') As ToolId, 
    STRING_AGG(Tool.Name,',') As ToolName,
    TGroup.Id AS GroupId, TGroup.Name AS GroupName, Base.Id,
    Base.Position, Task.Definition, Task.IsEvaluationActive
FROM dbo.CustomTask AS Task INNER JOIN
    dbo.TaskBase AS Base ON Task.Id = Base.Id LEFT OUTER JOIN
    dbo.TaskGroup AS TGroup ON Base.TaskGroupId = TGroup.Id LEFT OUTER JOIN
    dbo.ToolTaskGroupMapping AS Map ON TGroup.Id = Map.TaskGroupId LEFT OUTER JOIN
    dbo.Tool AS Tool ON Map.ToolId = Tool.Id) t1
GROUP BY t1.GroupId, t1.GroupName, t1.Id, t1.Position, t1.Definition, t1.IsEvaluationActive

在较旧的 SQL 服务器版本中,我们可以使用 SQLCLR 字符串聚合函数获得类似的语法,例如 the one mentioned in Aaron Bertrand's article 关于字符串连接:

select 
    dbo.GROUP_CONCAT(Tool.ID) As ToolId, 
    dbo.GROUP_CONCAT(Tool.Name) As ToolName,
    TGroup.Id AS GroupId, TGroup.Name AS GroupName, Base.Id,
    Base.Position, Task.Definition, Task.IsEvaluationActive
FROM dbo.CustomTask AS Task INNER JOIN
    dbo.TaskBase AS Base ON Task.Id = Base.Id LEFT OUTER JOIN
    dbo.TaskGroup AS TGroup ON Base.TaskGroupId = TGroup.Id LEFT OUTER JOIN
    dbo.ToolTaskGroupMapping AS Map ON TGroup.Id = Map.TaskGroupId LEFT OUTER JOIN
    dbo.Tool AS Tool ON Map.ToolId = Tool.Id) t1
GROUP BY t1.GroupId, t1.GroupName, t1.Id, t1.Position, t1.Definition, t1.IsEvaluationActive