Oracle - 在不使用 XMLAGG 的情况下将列值转换为逗号分隔值作为 CLOB
Oracle - convert column values to comma separated values as CLOB without using XMLAGG
我正在使用 Oracle 12.1。
我有一个 ID 列,我正在使用 group by 并希望将另一列(比如 NAME)中的值转换为逗号分隔的字符串作为 CLOB(不是 VARCHAR2,因为它限制为 4000 个字符)。
我尝试使用 LISTAGG 函数,但它失败了,因为逗号分隔的字符串超过 4000 个字符。 (LISTAGG有改进版限制溢出,但Oracle 12.1没有)
使用 XMLAGG,它可以工作,但我不想使用 XMLAGG,因为这个特定函数每 5 秒调用一次,并且有时会出现性能问题,并且偶尔会出现“ORA-04036:实例使用的 PGA 内存”超过 PGA_AGGREGATE_LIMIT"
我想要的是-
- 要么需要一种方法将列值转换为以逗号分隔的字符串作为 CLOB(不使用 LISTAGG、XMLAGG)
或
- 我可以跳过一些列值并使用“...”来告知还有更多值。
(假设我们可以只考虑 5 行而不是给定 ID 的所有行(按列分组))
提前致谢!
从 my answer here 开始,您可以编写自定义聚合函数将 VARCHAR2
聚合成 CLOB
:
CREATE OR REPLACE TYPE CLOBAggregation AS OBJECT(
value CLOB,
STATIC FUNCTION ODCIAggregateInitialize(
ctx IN OUT CLOBAggregation
) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateIterate(
self IN OUT CLOBAggregation,
value IN VARCHAR2
) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateTerminate(
self IN OUT CLOBAggregation,
returnValue OUT CLOB,
flags IN NUMBER
) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateMerge(
self IN OUT CLOBAggregation,
ctx IN OUT CLOBAggregation
) RETURN NUMBER
);
/
CREATE OR REPLACE TYPE BODY CLOBAggregation
IS
STATIC FUNCTION ODCIAggregateInitialize(
ctx IN OUT CLOBAggregation
) RETURN NUMBER
IS
BEGIN
ctx := CLOBAggregation( NULL );
RETURN ODCIConst.SUCCESS;
END;
MEMBER FUNCTION ODCIAggregateIterate(
self IN OUT CLOBAggregation,
value IN VARCHAR2
) RETURN NUMBER
IS
BEGIN
IF value IS NULL THEN
NULL;
ELSIF self.value IS NULL THEN
self.value := value;
ELSE
self.value := self.value || ',' || value;
END IF;
RETURN ODCIConst.SUCCESS;
END;
MEMBER FUNCTION ODCIAggregateTerminate(
self IN OUT CLOBAggregation,
returnValue OUT CLOB,
flags IN NUMBER
) RETURN NUMBER
IS
BEGIN
returnValue := self.value;
RETURN ODCIConst.SUCCESS;
END;
MEMBER FUNCTION ODCIAggregateMerge(
self IN OUT CLOBAggregation,
ctx IN OUT CLOBAggregation
) RETURN NUMBER
IS
BEGIN
IF self.value IS NULL THEN
self.value := ctx.value;
ELSIF ctx.value IS NULL THEN
NULL;
ELSE
self.value := self.value || ',' || ctx.value;
END IF;
RETURN ODCIConst.SUCCESS;
END;
END;
/
CREATE FUNCTION CLOBAgg( value VARCHAR2 )
RETURN CLOB
PARALLEL_ENABLE AGGREGATE USING CLOBAggregation;
/
那么你可以这样做:
SELECT id,
CLOBAGG( name ) AS names
FROM (
SELECT id,
name
FROM your_table
ORDER BY your_ordering_column
)
GROUP BY id;
或
I am fine with skipping some column values and use "..." to inform that there are more values. (lets say we can consider only 5 rows instead of all rows for given ID (group by column))
SELECT id,
LISTAGG(
CASE rn WHEN 6 THEN '...' ELSE name END,
','
) WITHIN GROUP (ORDER BY rn) AS names
FROM (
SELECT id,
name,
ROW_NUMBER() OVER (PARTITION BY id ORDER BY your_ordering_column) AS rn
FROM your_table
)
WHERE rn <= 6
GROUP BY id;
我正在使用 Oracle 12.1。 我有一个 ID 列,我正在使用 group by 并希望将另一列(比如 NAME)中的值转换为逗号分隔的字符串作为 CLOB(不是 VARCHAR2,因为它限制为 4000 个字符)。
我尝试使用 LISTAGG 函数,但它失败了,因为逗号分隔的字符串超过 4000 个字符。 (LISTAGG有改进版限制溢出,但Oracle 12.1没有)
使用 XMLAGG,它可以工作,但我不想使用 XMLAGG,因为这个特定函数每 5 秒调用一次,并且有时会出现性能问题,并且偶尔会出现“ORA-04036:实例使用的 PGA 内存”超过 PGA_AGGREGATE_LIMIT"
我想要的是-
- 要么需要一种方法将列值转换为以逗号分隔的字符串作为 CLOB(不使用 LISTAGG、XMLAGG)
或
- 我可以跳过一些列值并使用“...”来告知还有更多值。 (假设我们可以只考虑 5 行而不是给定 ID 的所有行(按列分组))
提前致谢!
从 my answer here 开始,您可以编写自定义聚合函数将 VARCHAR2
聚合成 CLOB
:
CREATE OR REPLACE TYPE CLOBAggregation AS OBJECT(
value CLOB,
STATIC FUNCTION ODCIAggregateInitialize(
ctx IN OUT CLOBAggregation
) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateIterate(
self IN OUT CLOBAggregation,
value IN VARCHAR2
) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateTerminate(
self IN OUT CLOBAggregation,
returnValue OUT CLOB,
flags IN NUMBER
) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateMerge(
self IN OUT CLOBAggregation,
ctx IN OUT CLOBAggregation
) RETURN NUMBER
);
/
CREATE OR REPLACE TYPE BODY CLOBAggregation
IS
STATIC FUNCTION ODCIAggregateInitialize(
ctx IN OUT CLOBAggregation
) RETURN NUMBER
IS
BEGIN
ctx := CLOBAggregation( NULL );
RETURN ODCIConst.SUCCESS;
END;
MEMBER FUNCTION ODCIAggregateIterate(
self IN OUT CLOBAggregation,
value IN VARCHAR2
) RETURN NUMBER
IS
BEGIN
IF value IS NULL THEN
NULL;
ELSIF self.value IS NULL THEN
self.value := value;
ELSE
self.value := self.value || ',' || value;
END IF;
RETURN ODCIConst.SUCCESS;
END;
MEMBER FUNCTION ODCIAggregateTerminate(
self IN OUT CLOBAggregation,
returnValue OUT CLOB,
flags IN NUMBER
) RETURN NUMBER
IS
BEGIN
returnValue := self.value;
RETURN ODCIConst.SUCCESS;
END;
MEMBER FUNCTION ODCIAggregateMerge(
self IN OUT CLOBAggregation,
ctx IN OUT CLOBAggregation
) RETURN NUMBER
IS
BEGIN
IF self.value IS NULL THEN
self.value := ctx.value;
ELSIF ctx.value IS NULL THEN
NULL;
ELSE
self.value := self.value || ',' || ctx.value;
END IF;
RETURN ODCIConst.SUCCESS;
END;
END;
/
CREATE FUNCTION CLOBAgg( value VARCHAR2 )
RETURN CLOB
PARALLEL_ENABLE AGGREGATE USING CLOBAggregation;
/
那么你可以这样做:
SELECT id,
CLOBAGG( name ) AS names
FROM (
SELECT id,
name
FROM your_table
ORDER BY your_ordering_column
)
GROUP BY id;
或
I am fine with skipping some column values and use "..." to inform that there are more values. (lets say we can consider only 5 rows instead of all rows for given ID (group by column))
SELECT id,
LISTAGG(
CASE rn WHEN 6 THEN '...' ELSE name END,
','
) WITHIN GROUP (ORDER BY rn) AS names
FROM (
SELECT id,
name,
ROW_NUMBER() OVER (PARTITION BY id ORDER BY your_ordering_column) AS rn
FROM your_table
)
WHERE rn <= 6
GROUP BY id;