【问题标题】:Oracle - convert column values to comma separated values as CLOB without using XMLAGGOracle - 不使用 XMLAGG 将列值转换为 CLOB 形式的逗号分隔值
【发布时间】:2021-11-01 11:24:23
【问题描述】:

我使用的是 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 行而不是所有行(按列分组))

提前谢谢你!

【问题讨论】:

  • 可能是this link可以帮助你。
  • 我有一个基于类型的 LISTAGG 实现,在github.com/connormcd/listagg_clob返回一个 CLOB
  • 我使用@ConnorMcDonald listagg_clob 已经有一段时间了,它可以正常工作。 (感谢侄女的那段代码),

标签: sql oracle plsql oracle12.1


【解决方案1】:

my answer here,你可以编写一个自定义聚合函数,将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;

【讨论】:

  • 谢谢 :) 我使用了第二种解决方案,即跳过一些列值并使用“...”
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-17
相关资源
最近更新 更多