Oracle: Way to aggregate concatenate an ungrouped column in grouped results

前端 未结 4 2102
感情败类
感情败类 2020-12-20 04:19

I have a query with several aggregate functions and then a few grouped columns. I want to take one of the grouped columns out of the group and perform some sort of aggregate

相关标签:
4条回答
  • 2020-12-20 04:38

    use listagg http://download.oracle.com/docs/cd/E14072_01/server.112/e10592/functions087.htm It is well documented and supported.

    0 讨论(0)
  • 2020-12-20 04:41

    Although I do not know of any built-in function capable of solving your problem, it appears that you can write your own aggregate function that can! Because I was curious, I tried my hand at implementing a custom, aggregate function that concatenates text with a delimiter:

    The type spec:

    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
    
    )
    

    The type body:

    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;
    

    The aggregate function:

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

    After all this, I was able to execute the following query:

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

    The result was a single row:

    Paynter, Adam
    
    0 讨论(0)
  • 2020-12-20 04:45

    Here is a nice article about different string aggregation techniques.

    I can add yet another method (XML-based):

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

    And in 11g Release 2 we finally have built-in LISTAGG function.

    0 讨论(0)
  • 2020-12-20 04:54

    Try out wm_concat(yourColumn)... it is available depending on your db version. It is not an officially documented function, but does the same thing as many of the other functions listed above.

    This makes a comma separated list. To get the new line character you can surround it with replace()

    Another tip is to use distinct within the wm_concat() such as wm_concat(distinct yourColumn) so that you don't get duplicates.

    0 讨论(0)
提交回复
热议问题