You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用SQL Server按客户编号将多行客户信息聚合为单行?

解决SQL Server中按客户编号聚合多行数据为单行的问题

针对你的需求,我们可以通过SQL Server的聚合函数和字符串拼接功能来实现,下面分两种常见场景给出解决方案:

场景1:合并客户名称,产品列取存在的有效值(优先保留YES)

如果希望产品列只要有一行是YES就显示YES,有NO但没有YES就显示NO,否则显示NULL,可以用MAX()聚合函数(因为字符串排序中YES > NO > NULL),配合STRING_AGG()(SQL Server 2017及以上版本支持)来合并客户名称:

SELECT
    MIN(ID) AS ID, -- 取最小的ID作为结果行的ID
    CUST_NUM,
    STRING_AGG(CUSTOMER_NAME, ',') WITHIN GROUP (ORDER BY ID) AS CUSTOMER_NAME, -- 按ID顺序合并名称
    MAX(BOUGHT_PRODUCT_A) AS BOUGHT_PRODUCT_A,
    MAX(BOUGHT_PRODUCT_B) AS BOUGHT_PRODUCT_B,
    MAX(BOUGHT_PRODUCT_C) AS BOUGHT_PRODUCT_C,
    MAX(BOUGHT_PRODUCT_D) AS BOUGHT_PRODUCT_D
FROM
    YourTableName -- 替换成你的实际表名
GROUP BY
    CUST_NUM;

执行这个查询后,对于你的示例数据,产品列BOUGHT_PRODUCT_A会返回YES(因为第二、三行都是YES),BOUGHT_PRODUCT_D返回YES——这和你给出的期望结果略有不同,推测可能是示例的特殊需求?如果确实需要保留第一行的产品列值,可以看场景2。

场景2:合并客户名称,产品列保留第一行的对应值

如果希望完全匹配你给出的期望结果,即产品列取该客户第一行(ID最小的行)的数值,同时合并所有客户名称,可以用窗口函数先标记第一行,再结合字符串拼接:

WITH RankedData AS (
    SELECT
        *,
        ROW_NUMBER() OVER (PARTITION BY CUST_NUM ORDER BY ID) AS RowRank, -- 按ID给每个客户的行排名
        STRING_AGG(CUSTOMER_NAME, ',') OVER (PARTITION BY CUST_NUM ORDER BY ID) AS AggregatedName -- 提前合并名称
    FROM
        YourTableName
)
SELECT
    ID,
    CUST_NUM,
    AggregatedName AS CUSTOMER_NAME,
    BOUGHT_PRODUCT_A,
    BOUGHT_PRODUCT_B,
    BOUGHT_PRODUCT_C,
    BOUGHT_PRODUCT_D
FROM
    RankedData
WHERE
    RowRank = 1; -- 只取每个客户的第一行

这个查询会返回你给出的期望结果:ID为1,客户名称合并为IHOP,I.H.O.P.,IHOP INC.,产品列和第一行完全一致。

兼容旧版本SQL Server(2016及以下)

如果你的SQL Server版本低于2017,不支持STRING_AGG(),可以用STUFF() + FOR XML PATH()的方式来拼接字符串:

对应场景1的旧版本写法:

SELECT
    MIN(ID) AS ID,
    CUST_NUM,
    STUFF((SELECT ',' + CUSTOMER_NAME
           FROM YourTableName t2
           WHERE t2.CUST_NUM = t1.CUST_NUM
           ORDER BY t2.ID
           FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '') AS CUSTOMER_NAME,
    MAX(BOUGHT_PRODUCT_A) AS BOUGHT_PRODUCT_A,
    MAX(BOUGHT_PRODUCT_B) AS BOUGHT_PRODUCT_B,
    MAX(BOUGHT_PRODUCT_C) AS BOUGHT_PRODUCT_C,
    MAX(BOUGHT_PRODUCT_D) AS BOUGHT_PRODUCT_D
FROM
    YourTableName t1
GROUP BY
    CUST_NUM;

对应场景2的旧版本写法:

WITH RankedData AS (
    SELECT
        *,
        ROW_NUMBER() OVER (PARTITION BY CUST_NUM ORDER BY ID) AS RowRank
    FROM
        YourTableName
),
AggregatedNames AS (
    SELECT
        CUST_NUM,
        STUFF((SELECT ',' + CUSTOMER_NAME
               FROM YourTableName t2
               WHERE t2.CUST_NUM = t1.CUST_NUM
               ORDER BY t2.ID
               FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '') AS AggregatedName
    FROM
        YourTableName t1
    GROUP BY
        CUST_NUM
)
SELECT
    rd.ID,
    rd.CUST_NUM,
    an.AggregatedName AS CUSTOMER_NAME,
    rd.BOUGHT_PRODUCT_A,
    rd.BOUGHT_PRODUCT_B,
    rd.BOUGHT_PRODUCT_C,
    rd.BOUGHT_PRODUCT_D
FROM
    RankedData rd
JOIN
    AggregatedNames an ON rd.CUST_NUM = an.CUST_NUM
WHERE
    rd.RowRank = 1;

你可以根据自己的SQL Server版本和实际需求选择对应的写法,记得把YourTableName替换成你的实际表名哦。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:51:36