当前位置: 代码网 > it编程>数据库>Oracle > Oracle数据库逗号拼接字段处理

Oracle数据库逗号拼接字段处理

2026年09月03日 Oracle 我要评论
1. 测试数据准备-- 可重复执行:先删旧表drop table test_employee_skills purge;create table test_employee_skills ( e

1. 测试数据准备

-- 可重复执行:先删旧表
drop table test_employee_skills purge;

create table test_employee_skills (
    emp_id         varchar2(10) primary key,
    emp_name       varchar2(50),
    dept_name      varchar2(50),
    skill_list     varchar2(200),
    project_list   varchar2(500)
);

insert into test_employee_skills values ('e001', '张三', '技术部', 'java,python,sql', '项目a,项目b');
insert into test_employee_skills values ('e002', '李四', '技术部', 'python,javascript,html', '项目c');
insert into test_employee_skills values ('e003', '王五', '产品部', 'axure,sql,python', '项目a,项目d,项目e');
insert into test_employee_skills values ('e004', '赵六', '设计部', 'photoshop,illustrator', '项目f');
insert into test_employee_skills values ('e005', '孙七', '技术部', 'java,spring,mysql,redis', '项目b,项目g,项目h');
insert into test_employee_skills values ('e006', '周八', '产品部', 'sql,tableau', '项目a,项目i');
insert into test_employee_skills values ('e007', '吴九', '技术部', null, '项目j');
insert into test_employee_skills values ('e008', '郑十', '设计部', 'photoshop', '');

注意:e008 的 project_list 写的是空字符串,但 oracle 中 ''​ 等价于 null(零长度字符即 null,这是 oracle 与 mysql/sql server 的关键差异)。因此它实际存的是 null:where project_list = ''​ 查不到它,判空必须用 is null​。

测试数据预览

emp_idemp_namedept_nameskill_listproject_list
e001张三技术部java,python,sql项目a,项目b
e002李四技术部python,javascript,html项目c
e003王五产品部axure,sql,python项目a,项目d,项目e
e004赵六设计部photoshop,illustrator项目f
e005孙七技术部java,spring,mysql,redis项目b,项目g,项目h
e006周八产品部sql,tableau项目a,项目i
e007吴九技术部null项目j
e008郑十设计部photoshopnull('' 即 null)

2. 判断是否包含

需求:查 skill_list 是否包含某个技能。核心思路是"前后补逗号后精确匹配",避免把 java 误配成 javascript。

2.1 instr(推荐,性能最好)

select emp_id, emp_name, dept_name, skill_list
from test_employee_skills
where instr(',' || skill_list || ',', ',python,') > 0;

执行结果

emp_idemp_namedept_nameskill_list
e001张三技术部java,python,sql
e002李四技术部python,javascript,html
e003王五产品部axure,sql,python

原理:字段前后各加逗号得到 ,java,python,sql,​,目标值也加逗号 ,python,​,再用 instr 找位置。null 拼接后仍为 null,instr 返回 null 不匹配——三个内建函数方案对 null 都天然安全,无需额外过滤。

优点:性能最好——纯字符串函数、无正则解析;null 天然安全,无需过滤。

缺点:需手工前后补逗号,写法略啰嗦;仅支持单值判断,多值需自行组合(见 2.5)。

适用版本:广泛兼容 | 性能:⭐⭐⭐⭐⭐

2.2 like

-- 前后加逗号匹配(推荐,避免误匹配)
select emp_id, emp_name, skill_list
from test_employee_skills
where ',' || skill_list || ',' like '%,java,%';

-- 直接使用通配符(可能误匹配,不推荐)
select emp_id, emp_name, skill_list
from test_employee_skills
where skill_list like '%java%';  -- 会误匹配 'javascript'

注意%java%​ 会把 'javascript' 也匹配进来;必须用第一种前后补逗号的写法。

优点:写法简单易读;同为纯字符串操作,性能接近 instr。

缺点:容易漏掉前后补逗号导致误匹配(%java%​ 会命中 javascript);语义不如 instr 直观。

适用版本:广泛兼容 | 性能:⭐⭐⭐⭐

2.3 regexp_like

-- 精确匹配(边界控制)
select emp_id, emp_name, skill_list
from test_employee_skills
where regexp_like(skill_list, '(^|,)sql(,|$)');

-- 匹配多个值
select emp_id, emp_name, skill_list
from test_employee_skills
where regexp_like(skill_list, '(^|,)(java|python)(,|$)');

正则说明(^|,)​ 匹配开头或逗号,(,|$)​ 匹配逗号或结尾,精确圈定独立技能项。多值 (java|python)​ 为 or(任一匹配)语义;and 写法见 2.5。

注意:技能名含正则元字符(如 .​、(​)时需转义;10g 起可用。

优点:一个表达式同时支持边界控制与多值((^|,)(java|python)(,|$)​),最灵活。

缺点:正则开销最大(官方博客:正则比其他字符串函数慢);技能名含元字符时需转义。

适用版本:10g+ | 性能:⭐⭐⭐

2.4 自定义函数(复用)

create or replace function find_in_set(
    p_value varchar2,
    p_str   varchar2,
    p_delim varchar2 default ','
) return number as
begin
    if p_str is null or p_value is null then
        return 0;
    end if;
    return instr(p_delim || p_str || p_delim, p_delim || p_value || p_delim);
end;
/

-- 用法一:判断是否包含
select emp_id, emp_name, skill_list
from test_employee_skills
where find_in_set('python', skill_list) > 0;

-- 用法二:查看位置(字符位置,非元素序号)
select emp_id, emp_name, skill_list,
       find_in_set('python', skill_list) as char_pos
from test_employee_skills;

语义说明:本函数借用 mysql 的 find_in_set 命名以方便记忆,但语义不同——mysql 版本返回 1 基的元素序号,本函数返回的是字符位置(instr 语义)。做判断时只看 > 0​ 即可,不要当作序号使用。

注意:p_value 若含分隔符会误判;数据量大时每行都调用函数,性能一般。

优点:一处定义、处处复用,业务 sql 最简洁;支持自定义分隔符。

缺点:需先创建函数;返回字符位置(非序号),易与 mysql find_in_set 混淆;每行调用函数,大表性能一般。

适用版本:广泛兼容(需先创建) | 性能:⭐⭐

2.5 多值匹配

-- or:掌握 'java' 或 'python'
select emp_id, emp_name, skill_list
from test_employee_skills
where instr(',' || skill_list || ',', ',java,') > 0
   or instr(',' || skill_list || ',', ',python,') > 0;

-- and:同时掌握 'java' 和 'sql'
select emp_id, emp_name, skill_list
from test_employee_skills
where instr(',' || skill_list || ',', ',java,') > 0
  and instr(',' || skill_list || ',', ',sql,') > 0;

说明:把 2.1 的 instr 条件用 or/and 组合即可;regexp_like 的多值写法见 2.3 第二个示例。

优点:instr 组合,语义清晰,无正则开销。

缺点:每多一个值就多一条 instr,条件多时冗长;性能随条件数线性下降。

适用版本:广泛兼容 | 性能:⭐⭐⭐⭐⭐

2.6 方案对比

方案适用版本性能推荐指数
instr广泛兼容⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
like广泛兼容⭐⭐⭐⭐⭐⭐⭐⭐
regexp_like10g+⭐⭐⭐⭐⭐⭐
自定义函数广泛兼容(需先创建)⭐⭐⭐⭐⭐

性能说明:拼接列无法走索引,以上均为全表扫描下的相对开销。数据量大、查询频繁时应考虑规范化表设计,而不是依赖 sql 技巧。

3. 一行转多行

需求:把每行 skill_list 拆成多行,每行一个技能。以下方案均假设值中不含分隔符本身——含逗号的值在 csv 语义下天然有歧义,任何方案都无解。

3.1 json_table(12c+,推荐)

select t.emp_id, t.emp_name, t.skill_list, j.skill
from test_employee_skills t,
     json_table(
         '["' || replace(t.skill_list, ',', '","') || '"]',
         '$[*]'
         columns skill varchar2(50) path '$'
     ) j
where t.skill_list is not null;

转换过程'java,python,sql'​ → '["java","python","sql"]'​ → 输出 3 行。

执行结果(部分):

emp_idemp_nameskill_listskill
e001张三java,python,sqljava
e001张三java,python,sqlpython
e001张三java,python,sqlsql

优点:性能最优;无层级、递归 hack,写法直观。

缺点:仅 12c+;手工拼接字符串遇 "​/\​ 报 ora-40441;值含逗号无解(所有方案通病)。

注意(重要) :这里是手工拼接 json 字符串,值中若含双引号或反斜杠会生成非法 json,报 ora-40441(json syntax error)。测试数据不含这些字符所以正常;真实数据若可能包含,需先 replace 转义(如 "​ → "​)。

适用版本:12c+(12.1.0.2 起) | 性能:⭐⭐⭐⭐⭐

3.2 xmltable(10g+)

-- 标准写法
select t.emp_id, t.emp_name, t.skill_list, x.skill
from test_employee_skills t,
     xmltable(
         '/rowset/row'
         passing xmltype('<rowset><row>' || replace(t.skill_list, ',', '</row><row>') || '</row></rowset>')
         columns skill varchar2(50) path '.'
     ) x
where t.skill_list is not null;

-- 简写(12c+ 隐式转换,字符串表达式直接作 xquery)
select t.emp_id, t.emp_name, t.skill_list, x.skill
from test_employee_skills t,
     xmltable(
         ('"' || replace(t.skill_list, ',', '","') || '"')
         columns skill varchar2(50) path '.'
     ) x
where t.skill_list is not null;

注意:两种写法都要处理 xml 特殊字符——值中含 &​、<​ 等会报 ora-19112(xquery 求值错误),需先转义(如 &​ → &amp;​)。简写只是语法糖,并不比标准写法更安全,值中同样不能含双引号。

优点:10g 起可用,兼容旧库;标准写法直观。

缺点:特殊字符需手工转义(&​、<​ 等,否则 ora-19112);实测性能明显更慢(约 10 倍);简写仅 12c+ 且难读。

适用版本:标准写法 10g+;简写 12c+ | 性能:⭐⭐⭐

3.3 regexp_substr + connect by(10g+)

select t.emp_id, t.emp_name, t.skill_list,
       trim(regexp_substr(t.skill_list, '[^,]+', 1, level)) as skill
from test_employee_skills t
where t.skill_list is not null
connect by level <= regexp_count(t.skill_list, ',') + 1
   and prior t.emp_id = t.emp_id
   and prior sys_guid() is not null;

关键点

  • regexp_substr(字段, '[^,]+', 1, level)​:取第 level 个逗号分隔值,level 即元素序号
  • prior t.emp_id = t.emp_id​:把层级树限制在每行内部,避免行间笛卡尔爆炸
  • prior sys_guid() is not null​:官方推荐做法(oracle 官方 sql 博客)——每行 guid 唯一,可避开 connect by 的循环检测;旧资料常见的 dbms_random.value​ 亦可,但官方用 sys_guid
  • 连续逗号(空元素)会被跳过:'a,,b'​ 只出 2 行,不会报错

注意:层级机制本身较重,大表不推荐(见 3.5 性能对比)。

优点:写法简洁;10g 起可用。

缺点:prior+sys_guid hack 难懂;层级机制重,大表慢(实测约为行生成器 5 倍耗时);regexp_count 仅 11g+。

适用版本:10g+(示例用 regexp_count 需 11g+;10g 改用 length-replace 计数) | 性能:⭐⭐⭐

3.4 lateral(12c+)

select t.emp_id, t.emp_name, t.skill_list, l.lvl, l.skill
from test_employee_skills t,
     lateral (
         select level as lvl,
                trim(regexp_substr(t.skill_list, '[^,]+', 1, level)) as skill
         from dual
         connect by level <= regexp_count(t.skill_list, ',') + 1
     ) l
where t.skill_list is not null;

说明:把 3.3 的层级逻辑搬进 dual 上的子查询,用 lateral 逐行关联。无需 prior 技巧、无 json/xml 转义问题,语法清晰;oracle 官方博客处理"列中分隔值转行"用的就是这条路线。

优点:语法清晰、官方博客同款路线;无 prior hack、无转义问题;性能良好(行生成器)。

缺点:仅 12c+;嵌套子查询逐行执行,极端大表需实测。

适用版本:12c+ | 性能:⭐⭐⭐⭐

3.5 方案对比

方案适用版本语法简洁度性能推荐指数
json_table12c+⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
lateral12c+⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
xmltable10g+(简写 12c+)⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
regexp+connect by10g+⭐⭐⭐⭐⭐⭐⭐⭐⭐

性能说明:行生成器(lateral 内部)与 json_table 均为高效路线;xml 与 connect_by_root 层级变体明显更慢(第三方实测 xml 约慢 10 倍、connect_by_root 约慢 40 倍以上)。星级为相对开销,拼接列本就无法走索引。

4. 选型总结

  • 12c+ 性能优先:json_table——最快,但注意 3.1 的特殊字符前提
  • 12c+ 可读性优先:lateral——无转义问题、无 hack,官方同款路线,团队易维护
  • 10g/11g 兼容:xmltable(记得转义)或 regexp+connect by(数据量小可接受)
  • 需要复用:把逻辑封装成自定义函数(参考 2.4 的 find_in_set)
  • 根本建议:数据量大、查询频繁时,应把拼接列规范化为子表,而非在 sql 里反复拆分

以上就是oracle数据库逗号拼接字段处理的详细内容,更多关于oracle逗号拼接字段的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com