当前位置: 首页 > news >正文

GBase 8a之listagg/string_agg 函数的反函数实现

GBase8a数据库中 listagg/string_agg 函数的反函数实现


一、业务场景背景
在日常数据开发中,我们经常会遇到这种场景:某张表的字段里存储了用逗号(或其他分隔符)拼接的多个值,比如商品分类、标签、关联系统名称等,业务上需要把这些逗号分隔的字符串拆分成多行数据,方便后续的统计分析、关联查询或ETL处理。
这就是我们常说的“字符串拆分”,也被称为listagg/string_agg的“反函数”操作。


不同数据库生态下,实现方案差异较大:
•Oracle 中常用 CONNECT BY 层级查询配合正则实现拆分;
•GBase 8a MPP 这类国产数据库也支持类似的层级查询语法;
•但在部分环境中,层级查询语法存在兼容性限制,或在数据量较大时性能不够稳定,这时候就需要更通用、可移植的实现方案。

二、问题分析与通用方案选型
1. 原方案(层级查询法)的局限性
你最开始使用的 Oracle/GBase 层级查询方案,核心逻辑是通过 CONNECT BY LEVEL 生成序列,再用 regexp_substr 按位置截取字符串:

SELECT id, regexp_substr(names, '[^,]+', 1, LEVEL) AS name FROM t_demo WHERE names IS NOT NULL CONNECT BY PRIOR id = id AND PRIOR rand() IS NOT null AND LEVEL <= length(names) - length(replace(names, ',', '')) + 1;


这种方案在小数据量下能快速实现需求,但存在几个明显的短板:
•兼容性差:依赖数据库的层级查询和正则扩展函数,无法直接迁移到不支持该语法的数据库;
•性能瓶颈:当单条记录的分隔项数量极多(比如上千个)时,层级查询会产生大量递归操作,执行效率下降明显;
•特殊字符风险:原方案对中文逗号、全角符号等分隔符的处理不够友好,容易出现拆分失败的情况。


2. 通用方案:数字辅助表法
我们最终选择的“数字辅助表+关联拆分”方案,是一种几乎兼容所有关系型数据库的通用实现,核心思路是:
•先创建一个存储连续整数的“数字辅助表”(numbers),生成从1到N的序列;
•用辅助表的数字,和原表做关联,关联条件为“数字 ≤ 分隔符数量+1”(也就是拆分后的行数);
•通过两次 SUBSTRING_INDEX 函数,按位置截取第N个分隔项;
•统一分隔符格式,兼容全角逗号、半角逗号等不同写法。


这种方案的优势在于:
•兼容性极强:不依赖任何数据库的特殊语法,MySQL、GBase、PostgreSQL 等主流数据库都支持;
•性能稳定:基于普通的表关联实现,执行计划清晰,大数据量下的表现远优于层级递归;
•扩展性强:可以轻松适配不同分隔符(逗号、分号、竖线等),也支持处理全角/半角混合的场景。

三、完整实现步骤

下面我们一步步实现这个通用的字符串拆分方案。

步骤1:创建测试表与初始化数据 先创建一张测试表,模拟业务中常见的场景,同时包含半角逗号、全角逗号两种分隔符: SQL -- 先删除旧表(避免报错) DROP TABLE IF EXISTS t_demo; CREATE TABLE t_demo ( id INT , names VARCHAR(255) ); -- 插入测试数据,包含全角逗号、半角逗号两种分隔符 INSERT INTO t_demo VALUES (101, '苹果,香蕉,橙子'), -- 全角逗号分隔 (102, '电脑,手机'), -- 全角逗号分隔 (103, 'GBase,MySQL,Oracle,SQLServer'); -- 半角逗号分隔 步骤2:创建数字辅助表并初始化数据 数字辅助表是整个方案的核心,我们通过存储过程批量生成连续整数,方便后续使用: SQL -- 删除旧的辅助表 DROP TABLE IF EXISTS numbers; -- 创建数字辅助表,存储从1开始的连续整数 CREATE TABLE numbers ( n INT ); -- 如果存储过程已存在,先删除 DROP PROCEDURE IF EXISTS InsertNumbers; -- 修改语句结束符,避免存储过程内部的分号和外部冲突 DELIMITER $$ CREATE PROCEDURE InsertNumbers(IN max_num INT) BEGIN DECLARE i INT DEFAULT 1; -- 清空辅助表数据 TRUNCATE TABLE numbers; -- 处理异常参数:如果传入的max_num为空或小于1,默认生成100条数据 IF max_num IS NULL OR max_num < 1 THEN SET max_num = 100; END IF; -- 循环插入从1到max_num的连续整数 WHILE i <= max_num DO INSERT INTO numbers (n) VALUES (i); SET i = i + 1; END WHILE; END$$ DELIMITER ; -- 调用存储过程,生成1-100的数字序列 CALL InsertNumbers(100); 说明:max_num 的取值建议设置为业务中“单条记录最大分隔项数量”的上限,比如你的业务中最多单条记录有50个标签,设置为100就完全够用了。 步骤3:核心拆分SQL实现 通过子查询统一分隔符,再关联数字辅助表,完成字符串拆分: SQL SELECT t.id, -- 两次SUBSTRING_INDEX截取第n个分隔项,再用TRIM去除首尾空格 TRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(unified_names, ',', n.n), ',', -1)) AS name FROM ( -- 子查询:先把所有全角逗号替换为半角逗号,统一分隔符格式 SELECT id, REPLACE(names, ',', ',') AS unified_names FROM t_demo WHERE names IS NOT NULL ) t -- 关联数字辅助表:关联条件为n不超过拆分后的总行数 JOIN numbers n ON n.n <= LENGTH(t.unified_names) - LENGTH(REPLACE(t.unified_names, ',', '')) + 1 -- 按原表id和拆分顺序排序,结果更易读 ORDER BY t.id, n.n; gbase> SELECT -> t.id, -> TRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(unified_names, ',', n.n), ',', -1)) AS name -> FROM ( -> SELECT -> id, -> REPLACE(names, ',', ',') AS unified_names -> FROM t_demo -> WHERE names IS NOT NULL -> ) t -> JOIN numbers n -> ON n.n <= LENGTH(t.unified_names) - LENGTH(REPLACE(t.unified_names, ',', '')) + 1 -> ORDER BY t.id, n.n; +------+-----------+ | id | name | +------+-----------+ | 101 | 苹果 | | 101 | 香蕉 | | 101 | 橙子 | | 102 | 电脑 | | 102 | 手机 | | 103 | GBase | | 103 | MySQL | | 103 | Oracle | | 103 | SQLServer | +------+-----------+ 9 rows in set (Elapsed: 00:00:00.12)


可以看到,所有逗号分隔的字符串都被拆分成了独立的行,全角/半角逗号也被统一处理,拆分结果完全符合预期。

四、核心原理详解
1. 分隔符统一处理
REPLACE(names, ',', ',') 这一步是为了处理全角逗号和半角逗号混用的场景,把所有全角逗号替换为半角逗号,避免因为分隔符格式不统一导致拆分失败。
2. 计算拆分后的总行数
LENGTH(t.unified_names) - LENGTH(REPLACE(t.unified_names, ',', '')) + 1 这个公式,是计算单条记录需要拆分成多少行:
•原字符串长度:LENGTH(unified_names)
•去掉所有逗号后的字符串长度:LENGTH(REPLACE(unified_names, ',', ''))
•两者的差值,就是字符串中逗号的数量;
•逗号数量 + 1,就是拆分后的总行数(比如a,b,c有2个逗号,拆分后是3行)。
3. 两次 SUBSTRING_INDEX 截取
SUBSTRING_INDEX(str, delim, count) 函数的作用是按分隔符截取字符串:
•SUBSTRING_INDEX(unified_names, ',', n.n):截取字符串的前n.n个分隔项,比如n=2时,GBase,MySQL,Oracle会被截取为GBase,MySQL;
•再用 SUBSTRING_INDEX(..., ',', -1):从截取后的字符串中,取最后一个分隔项,也就是MySQL;
•最后用 TRIM() 去除可能存在的首尾空格,得到干净的结果。

http://www.jsqmd.com/news/861148/

相关文章:

  • 实测在ubuntu环境下调用taotoken api的延迟与稳定性表现
  • 焊接跟踪系统V1:操作机/数控钛板及有色金属修磨机/方管内纵缝焊接专机/焊接滚轮架/磁力爬行小车V1.0/管法兰焊接专机/选择指南 - 优质品牌商家
  • 3个核心操作:让网络架构可视化变得如此简单
  • 多模态大模型技术入门:让 AI 看见世界
  • 当跨境业务负载陡增,谷歌云AI算力在多市场布局里扮演什么角色
  • 2025口碑出众求职简历优化机构盘点 应届生央国企求职优选,中国烟草求职辅导/央企就业指导,求职简历优化机构哪个好 - 品牌推荐师
  • 2026国内200吨中水回用设备优质企业分析 行业实力深度盘点,全自动软化水设备,中水回用设备公司找哪家 - 品牌推荐师
  • Stable Diffusion 实战教程:从安装到图像生成
  • 6款优质降AIGC平台 降痕效果拉满
  • 05月冷轧精密钢管厂家精选集,助力工程高效推进,冷拔钢管/薄壁精密钢管/无缝方矩管,精密钢管源头厂家哪家强 - 品牌推荐师
  • 央国企求职简历优化哪家靠谱?资深从业者详解权威机构甄选标准,中国烟草求职辅导/应届生央国企上岸培训,求职简历优化机构推荐 - 品牌推荐师
  • 2026年5月新发布:浙江市场备受瞩目的实力泥浆泵品牌深度解析 - 2026年企业推荐榜
  • 流量洪峰与合规约束叠加时 奥创容量保障的落地边界观察
  • 怎么选北京老房翻新装修公司?2026年5月推荐五家评测案例与口碑 - 品牌推荐
  • 【PC】MToolsv0.1.0一款宝藏级电脑多媒体处理工具箱
  • 2025-2026年国内北京老房翻新装修公司推荐:五家排行产品专业评测解决厨卫漏水致邻里纠纷 - 品牌推荐
  • 技术突破:如何让ARM设备突破x86架构的束缚?
  • 2026现阶段玻璃转子流量计选型指南:聚焦实力厂家余姚伟创 - 2026年企业推荐榜
  • WPR仿真平台:三大核心功能助你零成本掌握机器人开发
  • 从物理光学到AI生成:揭秘玻璃折射率n=1.52如何映射为--s 750 + --iw 1.8的底层逻辑
  • 摆脱论文困扰!!2026 最新降AIGC软件测评与推荐
  • 2026年Q2高考志愿填报机构推荐排行实测解析:金榜如愿高报口碑如何、金榜如愿高考志愿填报怎么样、金榜如愿高考报考指导师正规吗选择指南 - 优质品牌商家
  • 炸裂!英伟达 Q1 狂赚 583 亿美元,AI 到底有多赚钱?网友:这是印钞机吧
  • 哪家国内人力资源外包公司靠谱?2026年5月推荐五家产品案例评测与评价 - 品牌推荐
  • 2026年Q2宁波婚纱摄影推荐:HELLO大象如何定义“靠谱”跟拍 - 2026年企业推荐榜
  • 2026环槽铆钉机选购推荐榜:多粒铆钉机/旋铆机/母线槽铆钉机/气动液压铆钉机/气动铆钉机/气压铆钉机/油压铆钉机/选择指南 - 优质品牌商家
  • 布料质感模拟私密工作流首度公开:融合PBR贴图预处理+MJ --tile指令+后期Subsurface Scattering叠加的三阶增强法
  • 2025-2026年莱茵优品电话查询:预约服务前请核实资质与合同条款 - 品牌推荐
  • 2025-2026年北京家装公司推荐:五家口碑好的产品评测新房入住避免甲醛痛点注意事项 - 品牌推荐
  • 2026年5月国内人力资源外包公司推荐:五家排名产品评测企业降本防用工风险 - 品牌推荐