Home » SQL & PL/SQL » SQL & PL/SQL » Purpose of ODCIAggregateMerge
Purpose of ODCIAggregateMerge [message #613214] Thu, 01 May 2014 02:30 Go to next message
user1231
Messages: 4
Registered: May 2014
Junior Member
Hi,
I've been looking into user-defined aggregate functions, but I can't seem to understand the purpose of ODCIAggregateMerge() function. It is said to be merging two aggregation contexts, however, I don't understand where the other aggregation context comes from. I have created an example of standard deviation aggregate function that takes as a parameter a column of numbers and returns the deviation. When I tested it out, it works properly. Can anybody provide me with some reasonable, simplified explanation of is that function used for/what the aggregate context is?
Thanks
Re: Purpose of ODCIAggregateMerge [message #613215 is a reply to message #613214] Thu, 01 May 2014 02:58 Go to previous messageGo to next message
Michel Cadot
Messages: 68645
Registered: March 2007
Location: Nanterre, France, http://...
Senior Member
Account Moderator

An aggregation context is a stream of aggregation that is (simplifying) a process that aggregates some rows.
When the function is executed in parallel you have several processes that work on a subset of the whole set of rows you selected, at the end Oracle has to merge the result of each parallel process.

Re: Purpose of ODCIAggregateMerge [message #613217 is a reply to message #613215] Thu, 01 May 2014 03:43 Go to previous messageGo to next message
user1231
Messages: 4
Registered: May 2014
Junior Member
So as I understood it, the merge function is triggered in such situation:
SELECT MyAggregate(col1), MyAggregate(col2) FROM MyTable

Is that right?
Re: Purpose of ODCIAggregateMerge [message #613218 is a reply to message #613217] Thu, 01 May 2014 03:49 Go to previous messageGo to next message
Michel Cadot
Messages: 68645
Registered: March 2007
Location: Nanterre, France, http://...
Senior Member
Account Moderator

No, it is used when "SELECT MyAggregate(col1) from MyTable" runs in parallel on MyTable.

Re: Purpose of ODCIAggregateMerge [message #613219 is a reply to message #613218] Thu, 01 May 2014 03:58 Go to previous messageGo to next message
user1231
Messages: 4
Registered: May 2014
Junior Member
Hmm... It's still a bit confusing. Could you give some example of it? But thanks for the previous answers!
Re: Purpose of ODCIAggregateMerge [message #613223 is a reply to message #613219] Thu, 01 May 2014 04:47 Go to previous messageGo to next message
Michel Cadot
Messages: 68645
Registered: March 2007
Location: Nanterre, France, http://...
Senior Member
Account Moderator

OK, here's an example, quite long to build a clear test case.
Here the aggregate function just does a SUM but it logs each procedure call in a TLOG table.

-- Clean
drop procedure plog;
drop sequence s;
drop function f;
drop table t purge;
drop table tlog purge;
drop trigger trg;
drop type typ;

-- Logging environment: a sequence, a table, an insert trigger and a procedure
create sequence s;
create table tlog (id integer, dt timestamp, value varchar2(100));
create or replace trigger trg before insert on tlog
for each row
declare
  seq integer;
begin
  select s.nextval into seq from dual;
  :new.id := seq;
  :new.dt := systimestamp;
end;
/
create or replace procedure plog (p_value in varchar2) is
  pragma autonomous_transaction;
begin
  insert into tlog (value) values (p_value);
  commit;
end;
/

-- Aggregation type
create or replace type typ as object 
(
  pid number,
  val number,

  static function ODCIAggregateInitialize (sctx IN OUT typ) return number,

  member function
    ODCIAggregateIterate (self IN OUT typ, value IN number)
    return number,

  member function ODCIAggregateMerge (self IN OUT typ, ctx2 IN typ) return number,

  member function
    ODCIAggregateTerminate (self IN typ, returnValue OUT number, flags IN number)
    return number

);
/
create or replace type body typ as

  static function ODCIAggregateInitialize (sctx IN OUT typ) return number
  is
  begin
    plog ('Initializing process: '||sys_context('userenv','sid'));
    sctx := typ(sys_context('userenv','sid'),0);
    return ODCIConst.Success;
  end;

  member function 
    ODCIAggregateIterate (self IN OUT typ, value IN number)
    return number
  is
  begin
    plog ('Process '||self.pid||' handling '||value||' previous total: '||self.val);
    self.val := self.val + value;
    return ODCIConst.Success;
  end;

  member function ODCIAggregateMerge (self IN OUT typ, ctx2 IN typ) return number
  is
  begin
    plog ('Merging '||self.pid||' ('||self.val||') and '||ctx2.pid||' ('||ctx2.val||')');
    self.val := self.val + ctx2.val;
    return ODCIConst.Success;
  end;

  member function 
    ODCIAggregateTerminate (self IN typ, returnValue OUT number, flags IN number)
    return number
  is
  begin
    plog ('Process '||self.pid||' terminates - total: '||self.val);
    returnValue := val;
    return ODCIConst.Success;
  end;

end;
/

-- Aggregate function
create or replace function f (input number)
return number
parallel_enable aggregate using typ;
/

SQL> -- Test
SQL> create table t as select level nb, cast(' ' as char(512)) val from dual connect by level <= 20;

Table created.

SQL> -- Run in serial
SQL> select 'Total: '||f(nb) res from t;
RES
-----------------------------------------------
Total: 210

1 row selected.

SQL> select value from tlog order by id;
VALUE
----------------------------------------------------------------------------------------------------
Initializing process: 144
Process 144 handling 1 previous total: 0
Process 144 handling 2 previous total: 1
Process 144 handling 3 previous total: 3
Process 144 handling 4 previous total: 6
Process 144 handling 5 previous total: 10
Process 144 handling 6 previous total: 15
Process 144 handling 7 previous total: 21
Process 144 handling 8 previous total: 28
Process 144 handling 9 previous total: 36
Process 144 handling 10 previous total: 45
Process 144 handling 11 previous total: 55
Process 144 handling 12 previous total: 66
Process 144 handling 13 previous total: 78
Process 144 handling 14 previous total: 91
Process 144 handling 15 previous total: 105
Process 144 handling 16 previous total: 120
Process 144 handling 17 previous total: 136
Process 144 handling 18 previous total: 153
Process 144 handling 19 previous total: 171
Process 144 handling 20 previous total: 190
Process 144 terminates - total: 210

SQL> -- Run in parallel
SQL> truncate table tlog;

Table truncated.

SQL> select /*+ parallel(t 2) */ 'Total: '||f(nb) res from t;
RES
-----------------------------------------------
Total: 210

1 row selected.

SQL> select value from tlog order by id;
VALUE
--------------------------------------------------------------------
Initializing process: 148
Process 148 handling 1 previous total: 0
Process 148 handling 2 previous total: 1
Process 148 handling 3 previous total: 3
Process 148 handling 4 previous total: 6
Process 148 handling 5 previous total: 10
Process 148 handling 6 previous total: 15
Process 148 handling 7 previous total: 21
Process 148 handling 8 previous total: 28
Process 148 handling 9 previous total: 36
Process 148 handling 10 previous total: 45
Process 148 handling 11 previous total: 55
Process 148 handling 12 previous total: 66
Process 148 handling 13 previous total: 78
Initializing process: 144
Merging 144 (0) and 148 (91)
Initializing process: 143
Process 143 handling 14 previous total: 0
Process 143 handling 15 previous total: 14
Process 143 handling 16 previous total: 29
Process 143 handling 17 previous total: 45
Process 143 handling 18 previous total: 62
Process 143 handling 19 previous total: 80
Process 143 handling 20 previous total: 99
Merging 144 (91) and 143 (119)
Process 144 terminates - total: 210

Re: Purpose of ODCIAggregateMerge [message #613360 is a reply to message #613223] Mon, 05 May 2014 10:13 Go to previous messageGo to next message
user1231
Messages: 4
Registered: May 2014
Junior Member
Sorry for my late response - thank you for the great example! I think I understand it now, and it isn't really relevant to my specific case I'm dealing with.
Re: Purpose of ODCIAggregateMerge [message #616014 is a reply to message #613360] Wed, 11 June 2014 10:22 Go to previous messageGo to next message
arielhhs
Messages: 2
Registered: June 2014
Location: SC
Junior Member
Just created an account to say thanks so much for this explanation Michel Cadot, it has been killing me not knowing what the heck this Merge thing meant! I thought the same thing as the OP, that it was used in these situations. "SELECT MyAggregate(col1), MyAggregate(col2) FROM MyTable" or something.

I have another question relating to these user defined aggregates..

I have made my own user defined aggregates which I only use as an analytic in a very specific context. Rather than give a dirty great block of code, I will just explain what the analytic does (or is supposed to do) and maybe you could even point out a way that this could have been achieved using built in analytics.

MyTable
----------------------------------------------------------------------------------------------------
ID | Value1 | UpperLimit | LowerLimit | MyAnalytic(Value1) | MyAnalytic2(Value1)| MyAnalytic3(Value1)
   |        |            |            | As FutureRowID     | As FutureRowValue1 |  As AboveOrBelow
----------------------------------------------------------------------------------------------------
1  | 102    |    4       |     5      |         5          |         108        |       A
2  | 104    |    4       |     4      |         4          |         99         |       B
3  | 101    |    4       |     3      |         5          |         108        |       A
4  | 99     |    2       |     1      |         5          |         108        |       A
5  | 108    |    5       |     4      |         10         |         104        |       B
6  | 111    |    6       |     7      |         10         |         104        |       B
7  | 110    |    1       |     8      |         9          |         102        |       B
8  | 105    |    4       |     5      |         -          |          -         |       -
9  | 102    |    2       |     5      |         10         |         104        |       A
10 | 104    |    4       |     5      |         -          |          -         |       -


So I hope you can see what I want here, but basically, for each row, i want to look forward down the table, only at rows which have:
FutureRow.ID > currentRow.ID
And
(
FutureRow.Value1 >= CurrentRow.Value1 + CurrentRow.UpperLimit
OR
FutureRow.Value1 <= CurrentRow.Value1 - CurrentRow.LowerLimit
)
And of these "FutureRows" that meet this criteria I want to grab the FIRST (I.E. the one with the lowest ID from those rows) ID, Value1 and say "was it an Above or Below row that met the criteria".

Now that is super easy with a correlated scalar subquery in the select list or maybe some kind of self join, however, i intend to apply this to very large amounts of data and performance is a factor. Currently, my dodgy user defined analytics performs this, except because my windowing clause would be:
"Rows between current row and unbounded following"
I believe it is looking all the way down the table, even once it hits a row that meets this criteria. How do i tell my analytic to stop looking down the table once it has found the row that matches the criteria? Or can this be done with built in analytics? (no sub-querying or self joins please).

If you would like code, just ask i will post it propmtly.
Re: Purpose of ODCIAggregateMerge [message #616019 is a reply to message #616014] Wed, 11 June 2014 10:42 Go to previous messageGo to next message
Michel Cadot
Messages: 68645
Registered: March 2007
Location: Nanterre, France, http://...
Senior Member
Account Moderator

Quote:
I believe it is looking all the way down the table, even once it hits a row that meets this criteria.


Do not believe, add trace in your function as I did then you will see (and not imagine) what is done.

As for your function(s), no I don't see what it/they does/do and I don't know what is your input table and how your MyAnalytic function can return 5 when you give it 102 (1st row).

Re: Purpose of ODCIAggregateMerge [message #616046 is a reply to message #616019] Thu, 12 June 2014 00:11 Go to previous message
arielhhs
Messages: 2
Registered: June 2014
Location: SC
Junior Member
Ok i don't know why i didn't just think to add the trace to it like you did Embarassed .. I will do that now. However my user defined analytic turned out to have severe performance issues.

As for whether or not this can be done with built in analytics, I will try to explain what i want to achieve and maybe you can help me:

So the input table is simply MyTable WITHOUT the columns that have the analytics. Sorry for the lack of clarity. Here is the example again which you can directly copy and paste, no DDL required (please note, i made errors in my first example so ignore it and use this):

With MyTable As (
  Select 1 As ID, 102 As VALUE1, 4 As UPPERLIMIT, 5 As LOWERLIMIT From DUAL
  Union All
  Select 2, 104, 4, 4 From DUAL
  Union All
  Select 3, 101, 4, 3 From DUAL
  Union All
  Select 4, 99, 2, 1 From DUAL
  Union All
  Select 5, 108, 5, 4 From DUAL
  Union All
  Select 6, 111, 6, 7 From DUAL
  Union All
  Select 7, 110, 1, 8 From DUAL
  Union All
  Select 8, 105, 4, 5 From DUAL
  Union All
  Select 9, 102, 2, 5 From DUAL
  Union All
  Select 10, 104, 4, 5 From DUAL  
)
Select  MT1.ID,
        MT1.VALUE1,
        MT1.UPPERLIMIT,
        MT1.LOWERLIMIT,
        Min(MT2.ID) As FutureRowID, -- This is logically equivalent to MyAnalytic(VALUE1)
        Min(MT2.VALUE1) Keep (Dense_Rank First Order By MT2.ID) As FutureRowValue1, -- MyAnalytic2(VALUE1)
        Case 
          When Min(MT2.VALUE1) Keep (Dense_Rank First Order By MT2.ID) >= MT1.VALUE1 + MT1.UPPERLIMIT
          Then 'A'
          When Min(MT2.VALUE1) Keep (Dense_Rank First Order By MT2.ID) <= MT1.VALUE1 - MT1.LOWERLIMIT
          Then 'B'
          Else Null
        End  As AboveOrBelow -- MyAnalytic3(VALUE1)
From    MyTable MT1 Left Outer Join MyTable MT2 
        On ( MT2.ID > MT1.ID
        And (MT2.VALUE1 >= MT1.VALUE1 + MT1.UPPERLIMIT Or MT2.VALUE1 <= MT1.VALUE1 - MT1.LOWERLIMIT) 
            ) 
Group By 
        MT1.ID,
        MT1.VALUE1,
        MT1.UPPERLIMIT,
        MT1.LOWERLIMIT; 


Normally I would use a Where Clause join with a (+) to perform the outer join but due to the OR condition, this seems to be the only way around that.

If that isn't immediately clear, here is another method which achieves identical results but using scalar correlated-subqueries

With MyTable As (
  Select 1 As ID, 102 As VALUE1, 4 As UPPERLIMIT, 5 As LOWERLIMIT From DUAL
  Union All
  Select 2, 104, 4, 4 From DUAL
  Union All
  Select 3, 101, 4, 3 From DUAL
  Union All
  Select 4, 99, 2, 1 From DUAL
  Union All
  Select 5, 108, 5, 4 From DUAL
  Union All
  Select 6, 111, 6, 7 From DUAL
  Union All
  Select 7, 110, 1, 8 From DUAL
  Union All
  Select 8, 105, 4, 5 From DUAL
  Union All
  Select 9, 102, 2, 5 From DUAL
  Union All
  Select 10, 104, 4, 5 From DUAL  
)
Select  MT1.*,

        ( Select  Min(MT2.ID)
          From    MyTable MT2
          Where   MT2.ID        > MT1.ID
          And     ( MT2.VALUE1  >= MT1.VALUE1 + MT1.UPPERLIMIT
            Or      MT2.VALUE1  <= MT1.VALUE1 - MT1.LOWERLIMIT) ) As FutureRowID, -- MyAnalytic(VALUE1)
          
        ( Select  Min(MT2.VALUE1) Keep (Dense_Rank First Order By MT2.ID)
          From    MyTable MT2
          Where   MT2.ID        > MT1.ID
          And     ( MT2.VALUE1  >= MT1.VALUE1 + MT1.UPPERLIMIT
            Or      MT2.VALUE1  <= MT1.VALUE1 - MT1.LOWERLIMIT) ) As FutureRowValue1, -- MyAnalytic2(VALUE1)
        
        ( Select  Case 
                    When Min(MT2.VALUE1) Keep (Dense_Rank First Order By MT2.ID) >= MT1.VALUE1 + MT1.UPPERLIMIT
                    Then 'A'
                    When Min(MT2.VALUE1) Keep (Dense_Rank First Order By MT2.ID) <= MT1.VALUE1 - MT1.LOWERLIMIT
                    Then 'B'
                    Else Null
                  End
          From    MyTable MT2
          Where   MT2.ID        > MT1.ID
          And     ( MT2.VALUE1  >= MT1.VALUE1 + MT1.UPPERLIMIT
            Or      MT2.VALUE1  <= MT1.VALUE1 - MT1.LOWERLIMIT) ) As AboveOrBelow -- MyAnalytic3(VALUE1)
From    MyTable MT1;


So for each row, we want to see which is the first "future row" (future being defined as having a greater ID than the current row) that has:
FutureRow.Value1 >= CurrentRow.Value1 + CurrentRow.UpperLimit
OR
FutureRow.Value1 <= CurrentRow.Value1 - CurrentRow.LowerLimit

So is there a way to perform this logic using built in analytics to avoid a self join / this subquerying nonsense?
Previous Topic: Need School Management Schema
Next Topic: Facing Issue with LAST_VALUE function.
Goto Forum:
  


Current Time: Wed Apr 24 10:47:45 CDT 2024