【问题标题】:Oracle: Way to aggregate concatenate an ungrouped column in grouped resultsOracle:在分组结果中聚合连接未分组列的方法
【发布时间】:2009-11-18 16:49:10
【问题描述】:

我有一个包含几个聚合函数和几个分组列的查询。我想从组中取出一个分组列,并对它拥有的所有 VARCHAR 值执行某种聚合“连接”。 (理想情况下是在一个新的用马车分隔的列表中)。

这是我的查询,我记下我想在哪里执行此操作:

SELECT rownum, F.*
FROM (SELECT 
  c.logical_type "MerchantType",
  c.merchant_id "MerchantID",
  c.m_name "MerchantName",
  m.m_name "TransferredBy", /* <----- Make this aggregate */
  SUM(DECODE(b.ba_price,null,0,DECODE(b.BILL_SRVC_ID,'CREDITCHANGE',0,b.ba_price))) "TotalValue", 
  sum(DECODE(b.ba_price,null,0,DECODE(b.BILL_SRVC_ID,'CREDITCHANGE',b.ba_price,0))) "LimitChange", 
  SUM(DECODE(b.ba_status,'bdone',1,0)) "TxnCount",
  sum(to_number(decode(substr(b.ba_merchant_freetext,1,10),'Commission',substr(b.ba_merchant_freetext, 12,(instr(b.ba_merchant_freetext,';',1,1)-12))))) "Commission"
FROM bill_auth0 b,
  merchant0 m,
  merchant0 c
WHERE 
  b.srvc_prod_id = 'TRANSFER'
    AND b.ba_channel = 'WPSS'
    AND b.ba_status     IN ('bdone')
    AND b.merchant_id    = m.merchant_id
    AND b.customer_id    = c.merchant_id
    AND b.ba_timestamp BETWEEN to_date( '11/01/2009', 'MM/DD/YYYY' ) 
        AND to_date( '11/17/2009', 'MM/DD/YYYY' )+1
GROUP BY 
  c.logical_type,
  c.merchant_id,
  c.m_name,
  m.m_name /* <-- Remove from Grouped By */
ORDER BY c.logical_type, c.merchant_id, m.m_name) F;

所以基本上我希望能够得到一个结果,其中“TransferredBy”看起来像: 商户1
商家2
商户3

如果此组行有 3 个单独的 m.m_name 匹配项。

【问题讨论】:

    标签: oracle aggregate concatenation


    【解决方案1】:

    这是一篇关于不同string aggregation techniques 的好文章。

    我可以添加另一种方法(基于 XML):

    select rtrim(
             extract(
               sys_xmlagg(
                 xmlelement("X",ename||', ')
               ),
               '/ROWSET/X/text()'
             ).getstringval(),
             ', '
           )
      from emp;
    

    在 11g 第 2 版中,我们终于有了内置的 LISTAGG 函数。

    【讨论】:

      【解决方案2】:

      虽然我不知道有任何内置函数能够解决您的问题,但似乎you can write your own aggregate function 可以!因为好奇,我尝试实现一个自定义的聚合函数,将文本与分隔符连接起来:

      类型规范:

      CREATE OR REPLACE TYPE TextConcatenation AS OBJECT
      (
      
        text VARCHAR2(10000),
        delimiter VARCHAR2(10),
        concatenation_count NUMBER,
      
        STATIC FUNCTION ODCIAggregateInitialize(actx IN OUT TextConcatenation) RETURN NUMBER,
      
        MEMBER FUNCTION ODCIAggregateIterate(self IN OUT TextConcatenation, val IN VARCHAR2) RETURN NUMBER,
      
        MEMBER FUNCTION ODCIAggregateTerminate(self IN TextConcatenation, returnValue OUT VARCHAR2, flags IN NUMBER) RETURN NUMBER,
      
        MEMBER FUNCTION ODCIAggregateMerge(self IN OUT TextConcatenation, ctx2 IN TextConcatenation) RETURN NUMBER
      
      )
      

      类型体:

      CREATE OR REPLACE TYPE BODY TextConcatenation AS
      
        STATIC FUNCTION ODCIAggregateInitialize(actx IN OUT TextConcatenation) RETURN NUMBER IS
        BEGIN
          IF actx IS NULL THEN
            actx := TextConcatenation('', ', ', 0); #substitute your own delimiter here in the second argument
          ELSE
            actx.text := '';
            actx.delimiter := ', '; # substitute your own delimiter here
            actx.concatenation_count := 0;
          END IF;
          RETURN ODCIConst.Success;
        END;
      
        MEMBER FUNCTION ODCIAggregateIterate(self IN OUT TextConcatenation, val IN VARCHAR2) RETURN NUMBER IS
        BEGIN
          IF self.concatenation_count > 0 THEN
            self.text := self.text || delimiter;
          END IF;
      
          self.text := self.text || val;
      
          self.concatenation_count := self.concatenation_count + 1;
      
          RETURN ODCIConst.Success;
        END;
      
        MEMBER FUNCTION ODCIAggregateTerminate(self IN TextConcatenation, returnValue OUT VARCHAR2, flags IN NUMBER) RETURN NUMBER IS
        BEGIN
          returnValue := text;
          RETURN ODCIConst.Success;
        END;
      
        MEMBER FUNCTION ODCIAggregateMerge(self IN OUT TextConcatenation, ctx2 IN TextConcatenation) RETURN NUMBER IS
        BEGIN
          IF ctx2.concatenation_count > 0 THEN
            IF self.concatenation_count > 0 THEN
              self.text := self.text || delimiter || ctx2.text;
            ELSE
              self.text := ctx2.text;
            END IF;
            self.concatenation_count := self.concatenation_count + ctx2.concatenation_count;
          END IF;
      
          RETURN ODCIConst.Success;
        END;
      
      END;
      

      聚合函数:

      CREATE OR REPLACE FUNCTION text_concatenate(text VARCHAR2) RETURN VARCHAR2 AGGREGATE USING TextConcatenation;
      

      在这一切之后,我能够执行以下查询:

      SELECT text_concatenate(name) FROM
      (
        SELECT 'Paynter' name FROM DUAL
        UNION ALL
        SELECT 'Adam' name FROM DUAL
      )
      

      结果是一行:

      Paynter, Adam
      

      【讨论】:

        【解决方案3】:

        使用 listagg http://download.oracle.com/docs/cd/E14072_01/server.112/e10592/functions087.htm 它有很好的文档记录和支持。

        【讨论】:

          【解决方案4】:

          试试 wm_concat(yourColumn)... 它是否可用取决于您的数据库版本。它不是一个正式记录的函数,但与上面列出的许多其他函数做同样的事情。

          这会生成一个逗号分隔的列表。要获取换行符,您可以用 replace()

          将其包围

          另一个技巧是在 wm_concat() 中使用 distinct,例如 wm_concat(distinct yourColumn),这样您就不会得到重复。

          【讨论】:

            猜你喜欢
            • 2018-06-24
            • 2015-09-09
            • 2012-09-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-08-30
            • 2014-05-29
            • 1970-01-01
            相关资源
            最近更新 更多