Oracle:聚合方法在分组结果中连接未分组的列

时间:2009-11-18 16:49:10

标签: oracle aggregate concatenation

我有一个包含多个聚合函数的查询,然后是一些分组列。我想从组中取出一个分组列,并对它具有的所有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”看起来像: Merchant1
商家2
商人3

如果有3个单独的m.m_name匹配到此Group Row。

5 个答案:

答案 0 :(得分:5)

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

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

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

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

答案 1 :(得分:3)

虽然我不知道任何能够解决您问题的内置函数,但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

答案 2 :(得分:1)

答案 3 :(得分:0)

忽略这个作为答案,这是对评论的回复(stackoverflow不允许我出于某种原因发表评论..我在记住我之前存在的帐户之前发布了这个问题)。

我相信版本是10g。

答案 4 :(得分:0)

尝试 wm_concat(yourColumn) ...根据您的数据库版本,它可用。它不是官方记录的功能,但与上面列出的许多其他功能完全相同。

这使逗号分隔列表。要获得新行字符,可以使用 replace()

将其包围

另一个提示是在wm_concat()中使用distinct,例如 wm_concat(distinct yourColumn),这样就不会出现重复。