我们正在从Oracle 10g迁移到环境数据库的18c。为了使事情复杂化,并非计划立即迁移所有环境,因此该应用程序必须同时支持这两种环境。发现的不兼容性之一是在10g中支持WM_Concat
,但在18c中不支持,而在18c中却支持ListAgg
(新的等效函数),但不支持10g。因此,我正在寻找一种暂时可以在两个数据库版本中使用的实现。
我的想法是10g中的wm_concat(myColumn)
与18c中的listagg(myColumn, ',')
等效,因此我想将wm_concat(myColumn)
定义为传递的新18c数据库中的函数到幕后的listagg(myColumn, ',')
并返回结果。这样,应用程序就可以安全地继续在10g和18c数据库上正常使用wm_concat
,直到所有环境都在18c上为止,之后可以将应用程序交换为使用listagg
和临时自定义{{ 1}}函数可以从18c数据库中删除,从而完成迁移。
总而言之,定义wm_concat
以使wm_concat
的行为与查询中的wm_concat(myColumn)
完全一样的正确方法是什么?
答案 0 :(得分:6)
[TL; DR]您无法在Oracle 18c中实现WM_CONCAT
的自定义版本,使其行为与LISTAGG
完全相同,但是您可以通过用户定义的聚合函数来达到目的。 / p>
WM_CONCAT
的语法为:
WM_CONCAT( expr )
您会发现WM_CONCAT
缺乏指定分隔符或ORDER BY
子句的能力。
如果要在更高版本中重新定义WM_CONCAT
,则可能最终会使用用户定义的聚合函数:
用户定义的对象:
CREATE OR REPLACE TYPE t_string_agg AS OBJECT
(
g_string VARCHAR2(32767),
STATIC FUNCTION ODCIAggregateInitialize(
sctx IN OUT t_string_agg
) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateIterate(
self IN OUT t_string_agg,
value IN VARCHAR2
) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateTerminate(
self IN t_string_agg,
returnValue OUT VARCHAR2,
flags IN NUMBER
) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateMerge(
self IN OUT t_string_agg,
ctx2 IN t_string_agg
) RETURN NUMBER
);
/
用户定义的对象主体:
CREATE OR REPLACE TYPE BODY t_string_agg IS
STATIC FUNCTION ODCIAggregateInitialize(
sctx IN OUT t_string_agg
) RETURN NUMBER
IS
BEGIN
sctx := t_string_agg(NULL);
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateIterate(
self IN OUT t_string_agg,
value IN VARCHAR2
) RETURN NUMBER
IS
BEGIN
SELF.g_string := self.g_string || ',' || value;
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateTerminate(
self IN t_string_agg,
returnValue OUT VARCHAR2,
flags IN NUMBER
) RETURN NUMBER
IS
BEGIN
returnValue := SUBSTR( SELF.g_string, 2 );
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateMerge(
self IN OUT t_string_agg,
ctx2 IN t_string_agg
) RETURN NUMBER
IS
BEGIN
SELF.g_string := SELF.g_string || ctx2.g_string;
RETURN ODCIConst.Success;
END;
END;
/
用户定义的聚合功能:
CREATE OR REPLACE FUNCTION wm_concat (p_input VARCHAR2)
RETURN VARCHAR2
PARALLEL_ENABLE AGGREGATE USING t_string_agg;
/
测试数据:
CREATE TABLE test_data ( id, value ) AS
SELECT 1, 'C' FROM DUAL UNION ALL
SELECT 1, 'A' FROM DUAL UNION ALL
SELECT 1, 'B' FROM DUAL UNION ALL
SELECT 2, 'D' FROM DUAL UNION ALL
SELECT 2, 'E' FROM DUAL;
测试查询:
SELECT id,
wm_concat( value ) AS wm_concat,
LISTAGG( value, ',' ) WITHIN GROUP ( ORDER BY ROWNUM ) AS listagg
FROM test_data
GROUP BY id;
输出:
ID | WM_CONCAT | LISTAGG -: | :-------- | :------ 1 | C,B,A | C,A,B 2 | D,E | D,E
您可以看到输出的顺序是不同的;因此您可以获得接近但不完全匹配的结果。
db <>提琴here
更新:
如果我们使用效率低下的聚合函数将所有值存储在集合中,然后调用LISTAGG
,那么我们可以更进一步:
CREATE TYPE stringlist IS TABLE OF VARCHAR2(4000);
CREATE OR REPLACE TYPE t_string_agg AS OBJECT
(
strings stringlist,
STATIC FUNCTION ODCIAggregateInitialize(
sctx IN OUT t_string_agg
) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateIterate(
self IN OUT t_string_agg,
value IN VARCHAR2
) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateTerminate(
self IN t_string_agg,
returnValue OUT VARCHAR2,
flags IN NUMBER
) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateMerge(
self IN OUT t_string_agg,
ctx2 IN t_string_agg
) RETURN NUMBER
);
/
CREATE OR REPLACE TYPE BODY t_string_agg IS
STATIC FUNCTION ODCIAggregateInitialize(
sctx IN OUT t_string_agg
) RETURN NUMBER
IS
BEGIN
sctx := t_string_agg( stringlist() );
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateIterate(
self IN OUT t_string_agg,
value IN VARCHAR2
) RETURN NUMBER
IS
BEGIN
SELF.strings.EXTEND;
SELF.strings( SELF.strings.COUNT ) := value;
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateTerminate(
self IN t_string_agg,
returnValue OUT VARCHAR2,
flags IN NUMBER
) RETURN NUMBER
IS
BEGIN
SELECT LISTAGG( column_value, ',' ) WITHIN GROUP ( ORDER BY column_value )
INTO returnValue
FROM TABLE( SELF.strings );
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateMerge(
self IN OUT t_string_agg,
ctx2 IN t_string_agg
) RETURN NUMBER
IS
BEGIN
SELF.strings := SELF.strings MULTISET UNION ALL ctx2.strings;
RETURN ODCIConst.Success;
END;
END;
/
CREATE OR REPLACE FUNCTION wm_concat (p_input VARCHAR2)
RETURN VARCHAR2
PARALLEL_ENABLE AGGREGATE USING t_string_agg;
/
然后:
SELECT id,
wm_concat( value ) AS wm_concat,
LISTAGG( value, ',' ) WITHIN GROUP ( ORDER BY value ) AS listagg
FROM test_data
GROUP BY id;
输出:
ID | WM_CONCAT | LISTAGG -: | :-------- | :------ 1 | A,B,C | A,B,C 2 | D,E | D,E
如果(且仅当)您要按字母顺序对值排序时,它将提供与LISTAGG
相同的输出;您不能指定其他顺序。它还需要从PL / SQL到SQL的上下文切换,以在最后一步执行聚合,因此它可能比纯PL / SQL聚合函数要慢,并且它将集合保存在内存中并继续扩展它,因此可以随着集合的增长(或在并行系统中合并)会增加额外的开销,这将进一步降低它的速度。
因此,它仍然不是LISTAGG
,但如果您愿意忍受性能方面的问题,它就会尽可能地接近您。
db <>提琴here