如何优化具有多个子查询的查询以进行字符串聚合?

问题描述 投票:0回答:3

这是我的询问:

SELECT
    wp.WorkplanID,
    STUFF((SELECT ', ' + ISNULL(ul.FirstName + ' ', '') + ISNULL(ul.LastName, '')
           FROM UserLogin ul
           INNER JOIN Vendors v ON ul.UserLoginID = v.UserLoginID
           INNER JOIN dbo.WorkPlanVendors wpv ON wpv.VendorID = CAST(v.VendorID AS INT)
           WHERE wpv.WorkPlanID = wp.WorkPlanID
           FOR XML PATH('')), 1, 2, '') AS VendorName,
    STUFF((SELECT ', ' + v.PrimaryPhone
           FROM UserLogin ul
           INNER JOIN Vendors v ON ul.UserLoginID = v.UserLoginID
           INNER JOIN dbo.WorkPlanVendors wpv ON wpv.VendorID = CAST(v.VendorID AS INT)
           WHERE wpv.WorkPlanID = wp.WorkPlanID
           FOR XML PATH('')), 1, 2, '') AS PrimaryPhone,
    STUFF((SELECT ', ' + ul.Email
           FROM UserLogin ul
           INNER JOIN Vendors v ON ul.UserLoginID = v.UserLoginID
           INNER JOIN dbo.WorkPlanVendors wpv ON wpv.VendorID = CAST(v.VendorID AS INT)
           WHERE wpv.WorkPlanID = wp.WorkPlanID
           FOR XML PATH('')), 1, 2, '') AS Email
FROM WorkPlan wp

获取数据需要 5 秒,但我想让它更快。

sql sql-server optimization sql-server-2014 string-aggregation
3个回答
0
投票

您对三列使用了相同的查询。您可以使用公共表表达式一次连接表并在查询中使用此引用,如下所示:

WITH CTE_WorkPlanVendors AS (
    SELECT wpv.WorkPlanID AS VendorWorkPlanID, ul.FirstName, ul.LastName, v.PrimaryPhone, ul.Email
    FROM UserLogin ul
    INNER JOIN Vendors v ON ul.UserLoginID = v.UserLoginID
    INNER JOIN dbo.WorkPlanVendors wpv ON wpv.VendorID = CAST(v.VendorID AS INT)
)
SELECT wp.WorkplanID,
       STUFF((SELECT ', ' + ISNULL(FirstName + ' ', '') + ISNULL(LastName, '')
              FROM CTE_WorkPlanVendors cte
              WHERE cte.VendorWorkPlanID = wp.WorkPlanID
              FOR XML PATH('')), 1, 2, '') AS VendorName,
       STUFF((SELECT ', ' + PrimaryPhone
              FROM CTE_WorkPlanVendors cte
              WHERE cte.VendorWorkPlanID = wp.WorkPlanID
              FOR XML PATH('')), 1, 2, '') AS PrimaryPhone,
       STUFF((SELECT ', ' + Email
              FROM CTE_WorkPlanVendors cte
              WHERE cte.VendorWorkPlanID = wp.WorkPlanID
              FOR XML PATH('')), 1, 2, '') AS Email
FROM WorkPlan wp;

0
投票

现有答案没有帮助,因为它们都多次查询基表。

您可以使用以下解决方案来查询一次基表并将其聚合起来:

  • APPLY
    中,使用
    FOR XML PATH('row')
    将整个数据集聚合到一个 XML 中。添加逗号。
  • SELECT
    中,查询每一列如下:
    • .query('row/TheColumn/text()')
      这将为您提供该名称的所有文本元素。
    • .value('text()[1]', 'nvarchar(max)')
      这不会转义任何 XML 编码。
    • 使用
      STUFF
      去掉前导逗号。
SELECT
  wp.WorkplanID,
  STUFF(x.x.query('row/Name/text()'        ).value('text()[1]', 'nvarchar(max)'), 1, 2, '') AS VendorName,
  STUFF(x.x.query('row/PrimaryPhone/text()').value('text()[1]', 'nvarchar(max)'), 1, 2, '') AS PrimaryPhone,
  STUFF(x.x.query('row/Email/text()'       ).value('text()[1]', 'nvarchar(max)'), 1, 2, '') AS Email
FROM (
    SELECT wp.WorkplanID
    FROM WorkPlan wp
    GROUP BY wp.WorkplanID
) wp
CROSS APPLY (
    SELECT
      ',' + ISNULL(FirstName + ' ', '') + ISNULL(LastName, '') AS Name
      ',' + v.PrimaryPhone AS PrimaryPhone,
      ',' + ul.Email AS Email
    FROM UserLogin ul
    INNER JOIN Vendors v ON ul.UserLoginID = v.UserLoginID
    INNER JOIN dbo.WorkPlanVendors wpv ON wpv.VendorID = CAST(v.VendorID AS INT)
    WHERE wpv.WorkPlanID = wp.WorkPlanID
    FOR XML PATH('row')
) x(x);

显然,在较新版本的 SQL Server 中,您不需要任何这些,您只需使用

STRING_AGG


0
投票
   ;

WITH CTE
AS (
    SELECT wp.WorkplanID
        ,ISNULL(ul.FirstName + ' ', '') + ISNULL(ul.LastName, '') AS FULLNAME
        ,v.PrimaryPhone
        ,ul.Email
    FROM WorkPlan wp
    INNER JOIN WorkPlanVendors wpv ON wpv.WorkPlanID = wp.WorkPlanID
    INNER JOIN Vendors v ON wpv.VendorID = CAST(v.VendorID AS INT)
    INNER JOIN UserLogin ul ON ul.UserLoginID = v.UserLoginID
    )
SELECT C.WorkplanID
    ,STUFF((
            SELECT ', ' + FULLNAME
            FROM CTE CFN
            WHERE C.WorkPlanID = CFN.WorkPlanID
            FOR XML PATH('')
            ), 1, 2, '') AS FULLNAME
    ,STUFF((
            SELECT ', ' + PrimaryPhone
            FROM CTE CPP
            WHERE C.WorkPlanID = CPP.WorkPlanID
            FOR XML PATH('')
            ), 1, 2, '') AS PrimaryPhone
    ,STUFF((
            SELECT ', ' + Email
            FROM CTE CEM
            WHERE C.WorkPlanID = CEM.WorkPlanID
            FOR XML PATH('')
            ), 1, 2, '') AS Email
FROM CTE C
© www.soinside.com 2019 - 2024. All rights reserved.