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"

我想要的是-

  1. 要么需要一种方法将列值转换为以逗号分隔的字符串作为 CLOB(不使用 LISTAGG、XMLAGG)

  1. 我可以跳过一些列值并使用“...”来告知还有更多值。 (假设我们可以只考虑 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;