GBase 8a
适配迁移
文章

南大通用GBase 8a 语法改造oracle12c字符串聚合函数LISTAGG WITHIN GROUP方法说明

发表于2026-02-03 13:36:35292次浏览9个评论

问题描述

在oracle迁移至8a数据库时,遇到以下SQL语法,不能直接迁移:

SELECT T.DATA_DATE,
       T.CONT_NO,
       SUBSTR(LISTAGG(TO_CHAR(T.NVOICE_NO), ';' ON OVERFLOW TRUNCATE '...' WITH COUNT) WITHIN GROUP(ORDER BY T.NVOICE_NO),1,200) AS NVOICE_NO,
	   T.INVOICE_TYPE,
       SUM(T.NVOICE_AMT)
    FROM ODS_IQP_NVOICE T
	WHERE T.DATA_DATE = CURDAY
		GROUP BY T.DATA_DATE, T.CONT_NO, T.INVOICE_TYPE;

 改造方式

1. 开始尝试这种方式改造:
SELECT 
    T.DATA_DATE,
    T.CONT_NO,
    CASE 
        WHEN CHAR_LENGTH(T.NVOICE_NO_LIST) <= 200 THEN
            T.NVOICE_NO_LIST
        ELSE
            CONCAT(
                SUBSTRING(T.NVOICE_NO_LIST, 1, 196),
                '...(',
                CAST((T.TOTAL_COUNT - ( 
                    LENGTH(SUBSTRING(T.NVOICE_NO_LIST, 1, 196)) - 
                    LENGTH(REPLACE(SUBSTRING(T.NVOICE_NO_LIST, 1, 196), ';', '')) 
                ) - 1) AS CHAR),  
                ')'
            )
    END AS NVOICE_NO,
    T.INVOICE_TYPE,
    T.NVOICE_AMT_SUM
FROM (
    SELECT 
        DATA_DATE,
        CONT_NO,
        INVOICE_TYPE,
        GROUP_CONCAT(NVOICE_NO ORDER BY NVOICE_NO SEPARATOR ';') AS NVOICE_NO_LIST,
        SUM(NVOICE_AMT) AS NVOICE_AMT_SUM,
        COUNT(*) AS TOTAL_COUNT
    FROM ODS_IQP_NVOICE
    WHERE DATA_DATE = CURDATE()
    GROUP BY DATA_DATE, CONT_NO, INVOICE_TYPE
) T;
但该方式容易存在GROUP_CONCAT聚合后内存越界的问题。

2. 后尝试通过以下方式改造:

SELECT T.DATA_DATE,
       T.CONT_NO, 
       GROUP_CONCAT(T.NVOICE_NO ORDER BY T.NVOICE_NO topN 200 SEPARATOR ';'  ) AS NVOICE_NO,
       T.INVOICE_TYPE, 
       SUM(T.NVOICE_AMT) AS NVOICE_AMT_SUM
  FROM ODS_IQP_NVOICE T 
 WHERE T.DATA_DATE = CURDATE()    
 GROUP BY T.DATA_DATE, T.CONT_NO, T.INVOICE_TYPE;
 
 即,取分组内前200的方式

 

 

 原理说明

(1)sql要实现什么功能

  • TO_CHAR(T.NVOICE_NO)

       将发票号字段NVOICE_NO转换为字符类型(避免拼接时类型异常)

 

  • LISTAGG(..., ';') WITHIN GROUP(ORDER BY T.NVOICE_NO)

       按T.NVOICE_NO排序后,将同组的发票号用;拼接。核心是 “分组内字符串拼接 ”  

 

  • ON OVERFLOW TRUNCATE '...' WITH COUNT

       LISTAGG 的溢出处理参数:

      ON OVERFLOW TRUNCATE:当拼接结果超长时截断

      '...':截断后追加的标识字符

      WITH COUNT:在截断后补充 “(剩余 N 个)” 的计数提示SUBSTR(...,1,200)最终将拼接结果截取前 200 个字符(双重保险,防止溢出)

 

(2)改造关键逻辑说明

  • 核心函数替换:

    • Oracle LISTAGG(字段, ';') WITHIN GROUP(ORDER BY 字段) → GBase8a语法GROUP_CONCAT(字段 ORDER BY 字段 SEPARATOR ';')

    • Oracle CURDAY → GBase8a CURDATE()(注意:Oracle 的CURDAY如果是自定义变量 / 函数,需对应调整)

  • 溢出截断模拟:

    • CHAR_LENGTH判断拼接后总长度是否超过 200(CHAR_LENGTH计算字符数,LENGTH计算字节数,根据编码选择)

    • 若超长:截取前 196 个字符,拼接...(剩余数量),保证最终长度≤200

    • 剩余数量计算逻辑:总条数 - 截断后包含的条数(通过 “总长度 - 去分隔符后长度” 得到分隔符数量,分隔符数量 + 1 = 已包含条数)

评论

登录后才可以发表评论
用户头像
山佳发表于 6个月前
学习了
枫溪发表于 6个月前
1
枫溪发表于 6个月前
2
枫溪发表于 6个月前
3
枫溪发表于 6个月前
4
枫溪发表于 6个月前
5
用户头像
郝老师发表于 6个月前
国产数据集替代的实用方案
BYRAN发表于 3个月前
该函数在9.5.3.28.13 版本中,增加了对 STRING_AGG 函数进行替代。 建议使用产品中该函数。
BYRAN发表于 3个月前
使用方式说明如下:
STRING_AGG(expression [separator delimiter]) within(order by col_name[asc/desc])
功能:配合group by聚合函数使用,将指定字符串内容以指定分隔符拼接在一起。功能同group_concat。
参数说明:
express 字符串类型的表达式,比如列字符串
separator delimiter 可选参数,用于指定分隔符,separator是关键字。如果未指定该参数,默认使用逗号作为分隔符。
within (order by col_name[asc/desc]) 用于指定排序列。
示例:
select id,string_agg(name separator '++') within(order by id) as test from t1 group by id;
功能限制说明如下:
1)函数支持数据类型理论上无限制边界,默认的系统参数group_concat_max_len为1024,部分用例会报越界,需要设置group_concat_max_len的范围大一些,用例执行成功 
示例:set global group_concat_max_len =10240;
2)暂不支持blob,longblob数据类型