Oracle -将列值转换为逗号分隔值作为CLOB,而不使用XMLAGG

mpgws1up  于 2023-04-11  发布在  Oracle
关注(0)|答案(2)|浏览(217)

我使用的是Oracle 12.1。我有一个ID列,我正在使用group by,并希望将另一列(比如NAME)中的值转换为逗号分隔的字符串作为CLOB(而不是VARCHAR 2,因为它限制为4000个字符)。
我尝试使用LISTAGG函数,但失败了,因为逗号分隔的字符串超过4000个字符。(有一个改进版本的LISTAGG来限制溢出,但在Oracle 12.1中不可用)
使用XMLAGG,它可以工作,但我不想使用XMLAGG,因为这个特殊的函数每5秒调用一次,有时会产生性能问题,偶尔也会出现“ORA-04036:示例使用的PGA内存超过PGA_AGGREGATE_LIMIT”
我想要的是-
1.或者需要一种方法将列值转换为逗号分隔的字符串作为CLOB(不使用LISTAGG,XMLAGG)

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

ftf50wuq

ftf50wuq1#

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;


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

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;
fcipmucu

fcipmucu2#

这里的答案不适合我的情况,因为我需要将其限制为单个SELECT查询,而不依赖于在数据库中创建的其他对象。
很多人建议使用XMLAGG来绕过4000个字符的限制。这确实有效,但问题是某些特殊字符会被XMLAGG“转义”,例如,〈变成&lt;,&变成&amp;等。
我在这里找到了解决这个问题的方法:https://forums.oracle.com/ords/apexds/post/xmlagg-apostrophe-and-quotes-issue-2107
如果使用XMLCAST Package XMLAGG,并将其强制转换为CLOB,则所有这些编码都是“未转义的”。
我发布这篇文章是为了更大的利益,以防有人(像我一样)沿着这里遇到我的限制,即不创建额外的对象来解决问题。

相关问题