How to sum up all rows based on a enumeration colu

2019-08-08 16:25发布

问题:

))Hi all, this is my table...

Currently Im using this function trigger...

IF NEW.timetype = 'Total' THEN
  SELECT SUM(timeelapse) FROM (SELECT DISTINCT ON (floor(timeindex)::int) floor(timeindex)::int timeindex, timeelapse FROM mytable WHERE pcnum = NEW.pcnum AND fnname = NEW.fnname AND timetype = 'Lap' ORDER BY 1, 2 DESC) alias INTO v_sumtimeelapse_fn;
    IF FOUND THEN
      NEW.timeelapse := v_sumtimeelapse_fn;
    ELSE
      RAISE EXCEPTION USING MESSAGE = 'There is not any previous row...';
    END IF;
END IF;

So whenever 'Total' is INSERTed on the timetype column it SUMs up all timeelapse FROM mytable WHERE pcnum = NEW.pcnum AND fnname = NEW.fname AND timetype = 'Lap' but only where timeindex has the highest centesimal value (in mytable they would be the first three skyblue highighted squares = 00:01:00, 00:03:00, 00:04:00 (1.001, 2.003, 3.003)) and place the result in the NEW.timeelapse (in mytable it would be the first blue highlighted square = 00:08:00)

The function makes the SUM correctly, but I would like to add two more rules to it...

  1. when 'Total' is INSERTed in timetype, if the function doesn't find any 'Lap' after a 'Start' (both with the same pcnum and fnname) then it should show an ERROR MESSAGE "'there isn't any Lap..." (in mytable it would be the red highlighted square). Note that there isn't any 'Lap' of fnname = 'bbbb' after 'Start' of fnname = 'bbbb'. I try to set my function to do so, using...

    RAISE EXCEPTION USING MESSAGE = 'There is not any previous row...'
    

but it just doesn't work, if I INSERT a 'Total' in that situation, it simple leaves the NEW.timeelapse NULL.

  1. The function should SUM the timeelapse with all its already set rules, but only FROM those which are below the last(descendent) 'Total' of the same pcnum and fnname (in mytable it will be the second blue highlighted square) Note that it SUM only the timeelapses that are after the last(descendet) 'Total' (in mytable it will be the fourth skyblue highlighted square).

Thanks Advanced.

回答1:

To exclude from the calculation rows earlier than last Total you can use the primary key in an accessory query. Declare new variable v_fnserial. Find a fnserial of the row with last occurence of 'Total' for given pcnum and fnname and assign the value to v_fnserial. In the main query add a condition fnserial > v_fnserial.

You should raise an exception when the main query returns null.

IF NEW.timetype = 'Total' THEN
    SELECT fnserial INTO v_fnserial
    FROM mytable 
    WHERE timetype = 'Total' AND pcnum = NEW.pcnum AND fnname = NEW.fnname
    ORDER BY fnserial DESC LIMIT 1;

    SELECT SUM(timeelapse) FROM (
        SELECT DISTINCT ON (floor(timeindex)::int) floor(timeindex)::int timeindex, timeelapse 
        FROM mytable 
        WHERE fnserial > coalesce(v_fnserial, 0) AND pcnum = NEW.pcnum AND fnname = NEW.fnname AND timetype = 'Lap' 
        ORDER BY 1, 2 DESC) alias 
    INTO v_sumtimeelapse_fn;
    IF v_sumtimeelapse_fn NOTNULL THEN
        NEW.timeelapse := v_sumtimeelapse_fn;
    ELSE
        RAISE EXCEPTION USING MESSAGE = 'There is not any previous row...';
    END IF;
END IF;