SQL Server多列分组下将SELECT结果合并到单列的实现问题
当然可以实现!这种多列分组后聚合字符串的需求在SQL里太常见了,哪怕你的lease_contacts表没有标识列也完全没问题。下面我针对几种主流数据库给你具体的实现方案,你可以根据自己用的数据库来选:
SQL Server(2017及以上版本)
SQL Server 2017之后引入了STRING_AGG函数,专门用来做字符串聚合,写法非常简洁:
-- 假设目标表名为target_table,列名对应如下 INSERT INTO target_table(property_name, unit_number, lease_start, combined_contacts) SELECT name, unit, lease_start_date, STRING_AGG(file_as_name, ', ') AS combined_contacts FROM lease_contacts GROUP BY name, unit, lease_start_date;
说明:直接把你需要分组的三个列(name、unit、lease_start_date)放在GROUP BY后面,STRING_AGG会自动把每组内的file_as_name用逗号加空格拼接成一个字符串,完全不需要依赖标识列。
MySQL(5.7及以上版本)
MySQL原生支持GROUP_CONCAT函数,用法同样简单:
INSERT INTO target_table(property_name, unit_number, lease_start, combined_contacts) SELECT name, unit, lease_start_date, GROUP_CONCAT(file_as_name SEPARATOR ', ') AS combined_contacts FROM lease_contacts GROUP BY name, unit, lease_start_date;
说明:GROUP_CONCAT默认用逗号分隔,这里显式指定SEPARATOR ', '是为了更清晰,分组逻辑和上面一致,按三个列分组即可。
PostgreSQL
PostgreSQL同样支持STRING_AGG函数,语法和SQL Server类似:
INSERT INTO target_table(property_name, unit_number, lease_start, combined_contacts) SELECT name, unit, lease_start_date, STRING_AGG(file_as_name, ', ') AS combined_contacts FROM lease_contacts GROUP BY name, unit, lease_start_date;
如果是非常老的PostgreSQL版本(9.0之前),可以用array_agg配合array_to_string实现:
INSERT INTO target_table(property_name, unit_number, lease_start, combined_contacts) SELECT name, unit, lease_start_date, array_to_string(array_agg(file_as_name), ', ') AS combined_contacts FROM lease_contacts GROUP BY name, unit, lease_start_date;
兼容老版本数据库(比如SQL Server 2016及以前)
如果你的数据库版本比较老,没有内置的字符串聚合函数,可以用FOR XML PATH的方式手动拼接:
INSERT INTO target_table(property_name, unit_number, lease_start, combined_contacts) SELECT lc1.name, lc1.unit, lc1.lease_start_date, -- 用STUFF去掉开头多余的逗号和空格 STUFF(( SELECT ', ' + file_as_name FROM lease_contacts AS lc2 -- 关联分组列,确保子查询只取当前组的记录 WHERE lc2.name = lc1.name AND lc2.unit = lc1.unit AND lc2.lease_start_date = lc1.lease_start_date FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)'), 1, 2, '') AS combined_contacts FROM lease_contacts AS lc1 GROUP BY lc1.name, lc1.unit, lc1.lease_start_date;
说明:这个方法通过子查询把同组的联系人姓名拼接成XML格式的字符串,再用value方法转成普通字符串,最后用STUFF去掉开头的, ,同样不需要标识列,靠分组列关联子查询即可。
总的来说,核心逻辑就是按你需要的三个列分组,然后用对应数据库的字符串聚合函数把file_as_name合并,和表有没有标识列完全无关。你之前卡壳可能是没找到合适的聚合函数,或者对多列分组的写法不太熟悉,其实GROUP BY后面直接跟多个列就能实现多维度分组啦!
内容的提问来源于stack exchange,提问作者John Kiernan

