ORACLE数据库中分组字符串相加

本文介绍了在Oracle数据库中实现字符串按分组相加的方法,包括使用PL/SQL过程、自定义聚合函数、分析函数及连接操作等多种技术手段。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ORACLE数据库中分组字符串相加

来自 http://blog.oracle.com.cn/index.php/233877/viewspace-5271.html

引用
--该测试脚本可以直接运行

--现在想把数据库中数据按照固定字段分组相加,这里总结了三种方法。

--创建测试表、添加测试数据

Sql代码 复制代码
  1. create table test(id varchar2(10),mc varchar2(50));   
  2. insert into test values('1','11111');   
  3. insert into test values('1','22222');   
  4. insert into test values('2','11111');   
  5. insert into test values('2','22222');   
  6. insert into test values('3','11111');   
  7. insert into test values('3','22222');   
  8. insert into test values('3','33333');   
  9. commit;  
create table test(id varchar2(10),mc varchar2(50));
insert into test values('1','11111');
insert into test values('1','22222');
insert into test values('2','11111');
insert into test values('2','22222');
insert into test values('3','11111');
insert into test values('3','22222');
insert into test values('3','33333');
commit;


--方法一:


Sql代码 复制代码
  1. set serveroutput on size 1000000   
  2. declare  
  3. union_mc varchar2(200);   
  4. begin  
  5. for cur_a in(select distinct id from test) loop   
  6. for cur_b in(select mc from test where id=cur_a.id) loop   
  7. union_mc:=union_mc||cur_b.mc;   
  8. end loop;   
  9. dbms_output.put_line(cur_a.id||chr(9)||union_mc);   
  10. union_mc := '';   
  11. end loop;   
  12. end;   
  13. /  
set serveroutput on size 1000000
declare
union_mc varchar2(200);
begin
for cur_a in(select distinct id from test) loop
for cur_b in(select mc from test where id=cur_a.id) loop
union_mc:=union_mc||cur_b.mc;
end loop;
dbms_output.put_line(cur_a.id||chr(9)||union_mc);
union_mc := '';
end loop;
end;
/


--方法二:

Sql代码 复制代码
  1. CREATE OR REPLACE function link(v_id varchar2) return varchar2 is  
  2.   union_mc varchar2(200);   
  3. begin  
  4.   for cur in (select mc from test where id = v_id) loop   
  5.     union_mc := union_mc || cur.mc;   
  6.   end loop;   
  7.   union_mc := rtrim(union_mc, 1);   
  8.   return union_mc;   
  9. end;   
  10.   
  11.   
  12. select id,link(id) from test group by id;   
  13. /  
CREATE OR REPLACE function link(v_id varchar2) return varchar2 is
  union_mc varchar2(200);
begin
  for cur in (select mc from test where id = v_id) loop
    union_mc := union_mc || cur.mc;
  end loop;
  union_mc := rtrim(union_mc, 1);
  return union_mc;
end;


select id,link(id) from test group by id;
/


--方法三:

/*从Oracle 9i开始,开发者可以创建用户自定义的合计函数,除了PL/SQL外,还可以使用任何Oralce所支持的语言(如C++或者Java)来创建合计函数。TYPE头定义必须包含ODCIAggregateInitialize、ODCIAggregateIterate、ODCIAggregateMerge和ODCIAggregateTerminate这四个接口函数。*/

/*Initialize函数对数据组各个需要处理的字段各运行一次。自然的,我需要为每一个值准备一个新的清单,所以需要初始化持久变量list,这里初始化值为null。*/

/*Iterate函数处理返回的行,所以实际上是由它来创建返回的值的清单。先测试list是否为空,如果为空,就把list直接设置为所引入的value值;如果list变量非空,则给list添加一个逗号后再插入value值,list的最大允许字符数32767。*/

/*Terminate函数在数据组的每个行的感兴趣字段数据被处理后执行。在这个函数中我只需简单的返回清单变量即可。*/

/*Merge函数,用来返回成功标记的。*/

/*创建自己的合计函数扩展了Oracle统计和文本处理能力。*/


Sql代码 复制代码
  1. create or replace type t_cat as object   
  2. (   
  3.   union_mc VARCHAR2(200),   
  4.   static function ODCIAggregateInitialize(sctx IN OUT t_cat) return number,   
  5.   member function ODCIAggregateIterate(self  IN OUT t_cat,   
  6.                                        value IN varchar2) return number,   
  7.   member function ODCIAggregateTerminate(self        IN t_cat,   
  8.                                          returnValue OUT varchar2,   
  9.                                          flags       IN number)   
  10.     return number,   
  11.   member function ODCIAggregateMerge(self IN OUT t_cat, ctx2 IN t_cat)   
  12.     return number   
  13. )   
  14. ;   
  15.   
  16. create or replace type body t_cat is static function ODCIAggregateInitialize(sctx IN OUT t_cat) return number is begin sctx := t_cat(''); return ODCIConst.Success; end;   
  17.   
  18. member function ODCIAggregateIterate(self IN OUT t_cat, value IN varchar2) return number is begin self.union_mc := self.union_mc || value; return ODCIConst.Success; end;   
  19.   
  20. member function ODCIAggregateTerminate(self IN t_cat, returnValue OUT varchar2, flags IN number) return number is begin returnValue := self.union_mc; return ODCIConst.Success; end;   
  21.   
  22. member function ODCIAggregateMerge(self IN OUT t_cat, ctx2 IN t_cat) return number is begin return ODCIConst.Success; endend;   
  23. /  
create or replace type t_cat as object
(
  union_mc VARCHAR2(200),
  static function ODCIAggregateInitialize(sctx IN OUT t_cat) return number,
  member function ODCIAggregateIterate(self  IN OUT t_cat,
                                       value IN varchar2) return number,
  member function ODCIAggregateTerminate(self        IN t_cat,
                                         returnValue OUT varchar2,
                                         flags       IN number)
    return number,
  member function ODCIAggregateMerge(self IN OUT t_cat, ctx2 IN t_cat)
    return number
)
;

create or replace type body t_cat is static function ODCIAggregateInitialize(sctx IN OUT t_cat) return number is begin sctx := t_cat(''); return ODCIConst.Success; end;

member function ODCIAggregateIterate(self IN OUT t_cat, value IN varchar2) return number is begin self.union_mc := self.union_mc || value; return ODCIConst.Success; end;

member function ODCIAggregateTerminate(self IN t_cat, returnValue OUT varchar2, flags IN number) return number is begin returnValue := self.union_mc; return ODCIConst.Success; end;

member function ODCIAggregateMerge(self IN OUT t_cat, ctx2 IN t_cat) return number is begin return ODCIConst.Success; end; end;
/


/*如果你的Oracle服务器没有配置成支持并行处理的方式,可以去掉参数PARALLEL_ENABLE*/

Sql代码 复制代码
  1. create or replace function catstr(v_mc varchar2) return varchar2   
  2.   PARALLEL_ENABLE   
  3.   AGGREGATE USING t_cat;   
  4. /   
  5.   
  6. select id, catstr(mc) from test group by id;  
create or replace function catstr(v_mc varchar2) return varchar2
  PARALLEL_ENABLE
  AGGREGATE USING t_cat;
/

select id, catstr(mc) from test group by id;


方法四:

Sql代码 复制代码
  1. select id,   
  2.        mc,   
  3.        row_number() over(partition by id order by id) rn_by_id,   
  4.        row_number() over(order by id) + id rn   
  5.   from test;  
select id,
       mc,
       row_number() over(partition by id order by id) rn_by_id,
       row_number() over(order by id) + id rn
  from test;


/*
利用分析函数,构造两列,做为连接的条件:按照id分组,RN-1等于PRIOR RN作为条件连接。
ID MC RN_BY_ID RN
---------- -------------------------------------------------- ---------- ----------
1 11111 1 2
1 22222 2 3
2 11111 1 5
2 22222 2 6
3 11111 1 8
3 22222 2 9
3 33333 3 10
*/

Sql代码 复制代码
  1. select id, ltrim(max(sys_connect_by_path(mc, ';')), ';') add_mc   
  2.   from (select id,   
  3.                mc,   
  4.                row_number() over(partition by id order by id) rn_by_id,   
  5.                row_number() over(order by id) + id rn   
  6.           from test)   
  7.  start with rn_by_id = 1   
  8. connect by rn - 1 = prior rn   
  9.  group by id   
  10.  order by id;  
select id, ltrim(max(sys_connect_by_path(mc, ';')), ';') add_mc
  from (select id,
               mc,
               row_number() over(partition by id order by id) rn_by_id,
               row_number() over(order by id) + id rn
          from test)
 start with rn_by_id = 1
connect by rn - 1 = prior rn
 group by id
 order by id;


/*
另用sys_connect_by_path函数实现字符串的连接,把最左边的分号去掉,即得到我们想要的结果
ID ADD_MC
---------- --------------------------------------------------------------------------------
1 11111;22222
2 11111;22222
3 11111;22222;33333
*/

Sql代码 复制代码
  1. select * from test;  
select * from test;


方法四的另一种写法

估计类似的写法还有很多,这个和上一个不同在于用的没有带有start with(filter功能)的connect,并借助level和first_value来实现。

Sql代码 复制代码
  1. SELECT distinct id,   
  2.                 ltrim(first_value(mc_add)   
  3.                       over(partition by id order BY l DESC),   
  4.                       ';')   
  5.   from (SELECT id, LEVEL l, sys_connect_by_path(mc, ';') mc_add   
  6.           from (select id || rownum rn, id || rownum - 1 rn_small, id, mc   
  7.                   from test)   
  8.         CONNECT BY PRIOR rn = rn_small);  
SELECT distinct id,
                ltrim(first_value(mc_add)
                      over(partition by id order BY l DESC),
                      ';')
  from (SELECT id, LEVEL l, sys_connect_by_path(mc, ';') mc_add
          from (select id || rownum rn, id || rownum - 1 rn_small, id, mc
                  from test)
        CONNECT BY PRIOR rn = rn_small);


变换一下:(考虑id不是数字的情况)

Sql代码 复制代码
  1. select id, ltrim(max(sys_connect_by_path(mc, ';')), ';')   
  2.   from (select id,   
  3.                mc,   
  4.                row_number() over(partition by id order by id) id1,   
  5.                row_number() over(order by id) + dense_rank() over(order by id) id2   
  6.           from test)   
  7.  start with id1 = 1   
  8. connect by prior id2 = id2 - 1   
  9.  group by id   
  10.  order by id; 
<think>好的,我现在需要解决用户在Oracle数据库中如何对同一行的多个字段进行求和的问题。首先,我需要回忆一下Oracle SQL的基本语法,特别是关于列的操作。用户提到的是同一行多列求和,所以应该使用简单的加法运算符将各个列相加。 首先,假设用户有一个表,包含多个数值类型的列,比如col1, col2, col3,他们想将这三列相加得到总和。这时候,基本的SQL语句应该是SELECT col1 + col2 + col3 AS total FROM table;。不过,需要考虑可能的NULL值,因为如果某一列为NULL,整个表达式的结果也会是NULL。这时候需要使用NVL函数将NULL转换为0,例如NVL(col1, 0) + NVL(col2, 0) + NVL(col3, 0)。 接下来,我需要检查用户提供的引用内容。引用[2]提到了使用UNION ALL来合并数据,但可能不直接相关。引用[3]提到GROUPING函数和ROLLUP/CUBE,但这更多用于分组汇总,而不是同行多列求和。不过,引用[3]中的NVL函数的使用是正确的,应该应用到当前的问题中。 然后,用户可能需要动态处理多个列,特别是当列的数量较多时,手动写每个列会比较麻烦。这时候可以考虑使用PL/SQL动态生成SQL语句,或者利用系统表查询列名,然后拼接成加法表达式。例如,从USER_TAB_COLUMNS中获取数值类型的列名,然后构造SELECT语句。 另外,用户可能需要处理不同数据类型的列,比如有些列可能是VARCHAR但存储数字,这时候需要先进行类型转换,使用TO_NUMBER函数,同时处理转换错误的情况,比如用DEFAULT 0 ON CONVERSION ERROR。 最后,需要给出示例代码,包括基本求和、处理NULL值、动态列处理以及可能的错误处理。同时,生成相关问题,比如如何处理动态列名、性能优化、忽略非数值列等。 总结步骤: 1. 基础SQL加法运算,处理NULL。 2. 使用NVL函数处理NULL。 3. 动态生成SQL应对多列。 4. 数据类型转换处理。 5. 提供示例代码和相关问题。</think>在Oracle数据库中实现同行多列数值相加,可通过以下方法实现: --- ### 方法1:基础加法运算符 直接使用`+`运算符对多列求和,需注意处理`NULL`值: ```sql SELECT NVL(列1, 0) + NVL(列2, 0) + NVL(列3, 0) AS 总和 FROM 表名; ``` - **说明**:`NVL`函数将`NULL`转为`0`,避免计算结果为`NULL`[^3]。 --- ### 方法2:动态生成列表达式 若列较多,可通过查询系统表动态构造表达式: ```sql SELECT 'SELECT ' || LISTAGG('NVL(' || column_name || ',0)', '+') WITHIN GROUP (ORDER BY column_id) || ' AS 总和 FROM 表名;' AS 动态SQL FROM user_tab_columns WHERE table_name = '表名' AND data_type IN ('NUMBER', 'INTEGER'); ``` - **说明**:通过`LISTAGG`拼接所有数值列,生成可执行的求和SQL[^3]。 --- ### 示例表及结果 假设表`sales`包含以下数据: | 产品ID | 季度1销售额 | 季度2销售额 | 季度3销售额 | |--------|------------|------------|------------| | 101 | 500 | NULL | 300 | | 102 | 200 | 150 | 180 | 执行以下SQL: ```sql SELECT 产品ID, NVL(季度1销售额,0) + NVL(季度2销售额,0) + NVL(季度3销售额,0) AS 年度总销售额 FROM sales; ``` 输出结果: ``` 产品ID | 年度总销售额 --------|------------- 101 | 800 102 | 530 ``` --- ### 扩展场景 1. **非数值列处理**:若存在非数值列(如字符串存储的数字),需使用`TO_NUMBER`转换: ```sql SELECT NVL(TO_NUMBER(列1 DEFAULT 0 ON CONVERSION ERROR), 0) + NVL(TO_NUMBER(列2 DEFAULT 0 ON CONVERSION ERROR), 0) AS 总和 FROM 表名; ``` 2. **性能优化**:对海量数据建议添加索引或使用物化视图[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值