tags:

views:

39

answers:

2

I have a database with columns looking like:

session | order | atype | amt
--------+-------+-------+-----
1       |  0    | ADD   | 10
1       |  1    | ADD   | 20
1       |  2    | SET   | 35
1       |  3    | ADD   | 10
2       |  0    | SET   | 30
2       |  1    | ADD   | 20
2       |  2    | SET   | 55

It represents actions happening. Each session starts at 0. ADD adds an amount, while SET sets it. I want a function to return the end value of a session, e.g.

SELECT session_val(1); --returns 45
SELECT session_val(2); --returns 55

Is it possible to write such a function/query? I don't know how to do any iteration-like things with SQL, or if it's possible at all.

+1  A: 

To use my query, you will need to begin each transaction with a SET 0 record. Also, you will not be able to add another atype, only SET and ADD will work as I suggest.

This query is untested.

The idea is to simply sum the latest (most ultimate) SET and every ADD which follows it:

SELECT SUM(amt) 
    WHERE session=X 
      AND order >= ( SELECT MAX(order) 
                         WHERE atype='SET' 
                           AND session=X );
Heath Hunnicutt
I didn't know the syntax for a stored proc so X is your sproc argument.
Heath Hunnicutt
+1: But you could correlate rather than reference the arg twice.
OMG Ponies
what does it do if there is no set? does it fail? is there a way in SQL to check for that?
Claudiu
If you can ensure that the first order value for a particular session value is ALWAYS zero, you can use this one without an initial SET 0 record by changing MAX(order) to COALESE(MAX(order),0).
Matthew Wood
+2  A: 

Well, this is hardly pretty but it's functional:

select sum(amt) as session_val
from (
  select segment,
         max(segment) over() as max_segment,
         amt
  from (
    select sum(case when atype = 'SET' then 1 else 0 end)
               over(order by "order") as segment,
           amt
    from command
    where session = 2
  ) x
) x
where segment = max_segment

It is quite simple in PL/pgsql though:

create function session_val(session int) returns int stable strict
language plpgsql as $$
declare
  value int := 0;
  rec command%rowtype;
begin
  for rec in select * from command where command.session = session_val.session loop
    if rec.atype = 'SET' then
      value := rec.amt;
    elsif rec.atype = 'ADD' then
      value := value + rec.amt;
    end if;
  end loop;
  return value;
end $$;

So take your pick, I guess.

araqnid
I'd do the pl/pgsql, if performance allowed it, as its 1000 times clearer.
rfusca