----- Original Message ----- 
From: "Russell Horn" <[EMAIL PROTECTED]>
To: <mysql@lists.mysql.com>
Sent: Wednesday, May 25, 2005 8:02 AM
Subject: Cumulative Totals


> I have a pretty simple table with a list of payments, not much more
> than:
>
> paymentID | amount | paymentDate
> 1   | 123    | 2005-01-10
> 2   | 77    | 2005-01-13
> 3   | 45    | 2005-02-16
> 4   | 13    | 2005-02-17
>
>
> I can get totals per month using a query like:
>
> SELECT SUM(amount) , DATE_FORMAT( `paymentDate` , '%Y-%m' )   FROM
> `payments`   GROUP BY DATE_FORMAT( payments . date , '%Y-%m' )
>
> That would give me:
>
> amount | paymentDate
> 200    | 2005-01
> 58     | 2005-02
>
> Is there any way to get a running cumulative total directly from mysql?
> Something like:
>
> amount | paymentDate
> 200    | 2005-01
> 258    | 2005-02
>

I'm not sure if this solution will appeal to you but I satisfied the same
requirement in DB2 by writing a UDF (User-Defined Function). The function
was called RunningSum() and was invoked as follows:

    select empno, salary, runningSum(salary) as running_sum from mytable

and gave the following result:

    EMPNO    SALARY    RUNNING_SUM
    00001     25000.00    25000.00
    00002    18000.00    43000.00
    00003    32000.00    75000.00

I think that's the sort of thing you want, right?

Now, I haven't actually tried porting my UDF over to MySQL so I'm not sure
if you could even make it work in MySQL. I believe you need to be using
Version 5.x of MySQL to be able to use UDFs and I'm still on Version 4.0.x.

The other thing you need for my UDF to work in MySQL would be a scratchpad
area. I've just skimmed the UDF section of the MySQL manual but can't see
anything that says clearly one way or the other whether they support
scratchpads or something functionally equivalent.

I wrote my function in Java, which is apparently not supported in MySQL but
the function itself is only a couple of lines long and doesn't use any
features unique to Java. You should be able to do the same work in C or C++
or (probably) SQL. The logic is very basic:

a. You set up a scratchpad or global variable that is initialized to zero. I
called this variable runningTotal. In my case, it is defined as a BigDecimal
but you should be able to use a comparable C/C++/SQL datatype, like double.
b. The function accepts a value from the column that is its argument. In the
example above, that is the salary column. The function adds that value to
the runningTotal variable.
c. The function is called once for each row of the table that satisfies the
query.
Each time it is called, the function returns runningTotal and displays it
(after the new value has been added to runningTotal).

In Java, this function looks like this (diagnostic code snipped). My
function was written to handle decimal numbers but you could change it very
easily to handle integers by changing 'BigDecimal' to 'int' each time it
occurs (and removing the import for java.math.BigDecimal).

============================================================================
===========================================================

package db2v8.udf.db2general;

import java.io.BufferedWriter;
import java.math.BigDecimal;

import COM.ibm.db2.app.UDF;


/**
 * Class RunningSum contains various methods for DB2 UDFs that involve
keeping running
 * totals of numeric columns.
 *
 * @version 1.0
 * @since 2005-03-03
 */
public class RunningSum extends UDF {

    /**
     * The scratchpad for the runningSum() method. It *must* be initialized
outside of
     * the method.
     */
    private BigDecimal runningTotal = new BigDecimal(0.0);


    /**
     * Method runningSum() is used to accumulate a running total for a given
column of a
     * table.
     *
     * <p>Given a database query that returns multiple rows and a numeric
input 'amount',
     * this function returns that the sum of the current amount and the
total of the
     * previous amounts in the same column.</p>
     *
     * <p>For example, given this table:
     * <xmp>
     * EMPNO    SALARY
     * 00001    25000.00
     * 00002    18000.00
     * 00003    32000.00
     * </xmp>
     * </p>
     *
     * <p>and this query:</p>
     * <xmp>select empno, salary, runningSum(salary) as running_sum from
mytable</xmp>
     *
     * <p>The query will return:</p>
     * <xmp>
     * EMPNO SALARY  RUNNING_SUM
     * 00001 25000.00  25000.00
     * 00002 18000.00  43000.00
     * 00003 32000.00  75000.00
     * </xmp>
     *
     * <p>In other words, the runningSum() function will return the actual
salary of the first
     * employee on the first row of the result, the sum of the salaries of
the first two
     * employees on the second row of the result, and the sum of the
salaries of the first
     * three employees on the third row of the result.</p>
     *
     * @version 1.0
     * @since 2005-03-03
     *
     * @return int the total of the values in the input column that have
been read so far
     * @throws Exception
     */
    public void runningSum(BigDecimal amount, BigDecimal sum) throws
Exception {

        try {

            /*
             * Add the amount from the current row to the sum.
             */
            runningTotal = runningTotal.add(amount);

            /* Return the value of the running total. */
            set(2, runningTotal);
        } catch (Exception excp) {
            excp.printStackTrace();
            throw new IllegalArgumentException("runningSum() - " +
excp.getMessage());
        }
    }

}

============================================================================
===========================================================

Like I said, you may not want to get into something like this but I think it
is the best way to handle your requirement. It will make it very easy for
you to get running totals in the future and helps keep your SQL very simple.

Rhino



-- 
No virus found in this outgoing message.
Checked by AVG Anti-Virus.
Version: 7.0.322 / Virus Database: 266.11.16 - Release Date: 24/05/2005


-- 
MySQL General Mailing List
For list archives: http://lists.mysql.com/mysql
To unsubscribe:    http://lists.mysql.com/[EMAIL PROTECTED]

Reply via email to