You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

SQL Server 2016无STRING_AGG函数时,如何合并TSQL临时查询结果列数据以减少输出行数

在SQL Server 2016中实现字符串分组合并(替代STRING_AGG)

SQL Server 2016确实没有自带的STRING_AGG函数,不过我们可以用经典的FOR XML PATH + STUFF组合来实现这个需求,这是2017版本之前处理字符串合并的标准方案。

思路分解

首先我们需要优化原查询中获取客户最后一笔订单的逻辑(原查询的关联子查询性能较差),用CTE(公共表表达式)先筛选出每个客户的最新订单,再基于这个结果关联其他表,最后对OrderContent进行分组拼接。

完整实现代码

WITH CustomerLastOrder AS (
    -- 先获取每个客户的最后一笔订单ID
    SELECT 
        CustomerID,
        SalesOrderID
    FROM (
        SELECT 
            CustomerID,
            SalesOrderID,
            -- 按客户分组,按订单日期倒序、订单ID倒序排名,确保取到最新的一笔
            ROW_NUMBER() OVER (PARTITION BY CustomerID ORDER BY OrderDate DESC, SalesOrderID DESC) AS rn
        FROM Sales.SalesOrderHeader
    ) AS sub
    WHERE rn = 1
)
SELECT 
    CAST(SOH.OrderDate AS DATE) AS DateYMD,
    P.LastName,
    P.FirstName,
    -- 分组合并OrderContent,用逗号分隔(可自行修改分隔符)
    STUFF(
        (
            SELECT ', ' + PP.[Name] + ' Qty: ' + CAST(SOD.OrderQty AS VARCHAR(10))
            FROM Sales.SalesOrderDetail AS SOD
            JOIN Production.Product AS PP ON SOD.ProductID = PP.ProductID
            WHERE SOD.SalesOrderID = SOH.SalesOrderID
            FOR XML PATH(''), TYPE
        ).value('.', 'NVARCHAR(MAX)'),
        1, 2, '' -- 移除开头多余的", "分隔符
    ) AS OrderContent
FROM Person.Person AS P
JOIN Sales.Customer AS SC ON P.BusinessEntityID = SC.PersonID
JOIN CustomerLastOrder AS CLO ON SC.CustomerID = CLO.CustomerID
JOIN Sales.SalesOrderHeader AS SOH ON CLO.SalesOrderID = SOH.SalesOrderID
GROUP BY CAST(SOH.OrderDate AS DATE), P.LastName, P.FirstName, SOH.SalesOrderID;

关键部分解释

  1. CustomerLastOrder CTE
    ROW_NUMBER()函数按客户分组,对每个客户的订单按日期倒序、订单ID倒序排名,只保留排名为1的订单(即最新订单)。相比原查询的关联子查询,这个方式只需要扫描一次Sales.SalesOrderHeader表,性能更优。

  2. STUFF + FOR XML PATH

    • FOR XML PATH(''), TYPE:将当前订单下的所有OrderContent行拼接成一个XML片段,TYPE参数可以避免特殊字符(如&<>)被转义成XML实体(比如&amp;)。
    • .value('.', 'NVARCHAR(MAX)'):把XML片段转换成普通的字符串类型。
    • STUFF(..., 1, 2, ''):移除拼接字符串开头多余的, 分隔符(第一个参数是目标字符串,第二个参数是起始位置,第三个是要删除的字符数,第四个是替换内容)。

注意事项

  • 如果需要修改分隔符(比如用; 代替, ),只需要修改子查询中的, STUFF函数里的删除字符数(比如分隔符是; 的话,删除字符数改成2即可)。
  • 如果OrderContent中包含大量特殊字符,TYPE + .value的方式能保证字符不被转义,比直接用FOR XML PATH('')拼接成VARCHAR更可靠。

内容的提问来源于stack exchange,提问作者ku4er99

火山引擎 最新活动