0

我正在使用 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. 我可以跳过一些列值并使用“...”来通知还有更多值。(假设我们只能考虑给定 ID 的 5 行而不是所有行(按列分组))

先感谢您!

4

1 回答 1

1

我在这里的回答中,您可以编写一个自定义聚合函数来将VARCHAR2s 聚合成一个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;

或者

我可以跳过一些列值并使用“...”来通知还有更多值。(假设我们只能考虑给定 ID 的 5 行而不是所有行(按列分组))

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;
于 2021-09-03T07:48:23.683 回答