On Sat, 2 Mar 2013 14:46:40 +0100
Gert Van Assche <ger...@datamundi.be> wrote:

> All, I don't know how to achieve this: I need to put the cumulative
> sum in a field, and create a group as soon as that cumulative sum is
> over a breakpoint value (10).

This was a bit of a challenge because the group definition is IMO a
little strange: The first value *after* the breakpoint is included in
the group.  That made the SQL more convoluted than it would be
otherwise.  

That said, the attached SQL does the job in one statement.  No triggers
are needed.  

$ sqlite3 db < running_sum.sql
TextField   ValueField  CumulativeValue  BreakPoint  GroupName 
----------  ----------  ---------------  ----------  ----------
A           2           2                10          1         
B           3           5                10          1         
C           2           7                10          1         
D           4           11               10          1         
E           5           5                10          2         
F           1           6                10          2         
G           1           7                10          2         
H           5           12               10          2         
I           11          11               10          3         
J           8           8                10          4         
K           2           10               10          4         

This lets you define the results in terms of the base data instead of
keeping a table of derived values.  No updates required, just a view.
Unless you have data-warehouse rowcounts or a very limited machine, it
should perform just fine, despite what one might guess, because the I/O
is limited to one table.  It should also outperform any per-row
solution.  

Two bits of advice if I may.  

Be careful of using triggers to maintain data consistency.  Triggers
are very good for enforcing referential integrity in situations that
DRI can't handle. Beyond that they get very complex and error prone.  

Using triggers to compute derived values signals redundancy in the
database.  That redundancy often is not as desirable as the
denormalize-for-performance crowd thinks.  Better to compute derived
values on demand (as in a view).  If that's demonstrated to be too
slow, a periodic update in a cron job or similar should provide better
thoughput than recomputing on every insert/update/delete.  You'll also
do yourself a favor by segregating derived values in a different table
that can be regenerated at will.  

My other suggestion is to eschew abstract nouns in column names.  Words
like data, field, and value add no meaning.  After all, it's a
database.  If it's not a value, what is it?  

This table, 

CREATE TABLE [Test] 
        ( Name
        , Value
        , RunningSum
        , Breakpoint /* (one word, small 'p') */ DEFAULT 10
        , GroupName
        , primary key (Name)
        ); 

is at least  as clear, wouldn't you say?  

HTH.  

--jkl

P.S. In case the listserv strips attachments, here it is in plain
text.  

[SQL]
select   A.TextField, A.ValueField
       , A.total - coalesce(C.GroupTotal, 0) as CumulativeValue
       , A.Breakpoint
--       , coalesce(C.GroupTotal, 0) as GroupTotal
       , coalesce(B.GroupName, A.GroupName) as GroupName
from (
        select   a.*, sum(b.ValueField) as total
               , 1 + sum(b.ValueField) / (1+a.Breakpoint) as GroupName
        from Test as a join Test as b
        on a.TextField >= b.TextField
        group by a.TextField
                , a.ValueField
                , a.CumulativeValue
                , a.BreakPoint
) as A
left join (
        select   a.*, sum(b.ValueField) as total
               , 1 + sum(b.ValueField) / (1 + a.Breakpoint) as GroupName
        from Test as a join Test as b
        on a.TextField >= b.TextField
        group by a.TextField
                , a.ValueField
                , a.CumulativeValue
                , a.BreakPoint
) as B
on B.TextField = (select max(TextField) from Test where TextField <
A.TextField) left join (
        select    1 + T.GroupName as GroupName
                , max(T.total) as GroupTotal
        from (
                select    A.TextField
                        , A.ValueField
                        , A.total
                        , coalesce(B.GroupName, 1) as GroupName
                from (
                        select   a.*, sum(b.ValueField) as total
                               , 1 + sum(b.ValueField) / (1 +
a.Breakpoint) as GroupName from Test as a join Test as b
                        on a.TextField >= b.TextField
                        group by  a.TextField
                                , a.ValueField
                                , a.CumulativeValue
                                , a.BreakPoint
                                ) as A
                left join (
                        select   a.*, sum(b.ValueField) as total
                               , 1 + sum(b.ValueField) / (1 +
a.Breakpoint) as GroupName from Test as a join Test as b
                        on a.TextField > b.TextField
                        group by  a.TextField
                                , a.ValueField
                                , a.CumulativeValue
                                , a.BreakPoint
                ) as B
                on B.TextField = ( select max(TextField) 
                                   from Test 
                                   where TextField <= A.TextField  )
        ) as T
        group by GroupName
        ) as C
on B.GroupName = C.GroupName
;
[LQS]
> 
> This is an example table:
> 
> CREATE TABLE [Test] (TextField, ValueField, CumulativeValue,
> BreakPoint DEFAULT 10, GroupName);
> insert into [Test] values('A', '2', null, '10');
> insert into [Test] values('B', '3', null, '10');
> insert into [Test] values('C', '2', null, '10');
> insert into [Test] values('D', '4', null, '10');
> insert into [Test] values('E', '5', null, '10');
> insert into [Test] values('F', '1', null, '10');
> insert into [Test] values('G', '1', null, '10');
> insert into [Test] values('H', '5', null, '10');
> insert into [Test] values('I', '11', null, '10');
> insert into [Test] values('J', '8', null, '10');
> insert into [Test] values('K', '2', null, '10');
> 
> I'd like to end up with a table that looks like this:
> 
> ?TextField ValueField CumulativeValue BreakPoint GroupName
> A 2 2 10 1
> B 3 5 10 1
> C 2 7 10 1
> D 4 11 10 1
> E 5 5 10 2
> F 1 6 10 2
> G 1 7 10 2
> H 5 12 10 2
> I 11 11 10 3
> J 8 8 10 4
> K 2 2 10 4
> 
> I spent hours trying to update the CumulativeValue field untill the
> BreakPoint value is crossed, and restarting the cumulative counter,
> but I have too little sql knowledge to do this.
> 
> Could anyone help me?
> 
> thanks
> 
> gert
> _______________________________________________
> sqlite-users mailing list
> sqlite-users@sqlite.org
> http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users
_______________________________________________
sqlite-users mailing list
sqlite-users@sqlite.org
http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users

Reply via email to