On Fri, Jul 18, 2008 at 2:00 PM, Jason Yergeau <[EMAIL PROTECTED]> wrote:
> I'm having trouble working through a data problem.  Any tips or
> keywords that might clue me into a known pattern would be incredibly
> appreciated!
>
> I have about 500,000 users in my system.  Each user has a points
> balance, updated by a system over which I have no control.  I'd like
> to track changes to each user's point balance over time by taking
> timestamped snapshots of their balance, and saving it into a new
> table.
>
> It's easy to take the snapshot:
>
> insert into balances (userid, points) select userid, points from users;
>
> This quickly takes the points field from my users table, and saves it
> into a balances table, which saves the data along with a timestamp.  I
> can run that query on a regular basis without overly taxing my system.
>
> The first time its run, I get 500,000 rows of data.  That's fine.  But
> the next time I run a query, I only want to save the differences in
> balance.
>
> Anyone have any tips?
>
> Best,
> Jason
>

I'm sure there is a way to write this in a single query, but I bet it
it is ugly as heck, probably with a bunch of derived tables. Here is a
solution using a stored proc:

mysql> DROP TABLE users, balances;
Query OK, 0 rows affected (0.19 sec)

mysql> CREATE TABLE users(
    ->  userid int,
    ->  points int);
Query OK, 0 rows affected (0.13 sec)

mysql> CREATE TABLE balances(
    ->  userid int,
    ->  points int,
    ->  timestamp_dump timestamp);
Query OK, 0 rows affected (0.20 sec)

mysql> DROP PROCEDURE IF EXISTS pull_dump;
Query OK, 0 rows affected (0.00 sec)

mysql> DELIMITER //
mysql> CREATE PROCEDURE pull_dump ()
    -> BEGIN
    ->  DECLARE done,alt_done INT DEFAULT 0;
    ->  DECLARE users_userid,users_points,balances_points INT;
    ->  DECLARE cur1 CURSOR FOR SELECT userid,points FROM users;
    ->  DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done = 1;
    ->
    ->  OPEN cur1;
    ->  REPEAT
    ->          FETCH cur1 INTO users_userid,users_points;
    ->          IF NOT done THEN
    ->                  SET alt_done = done;
    ->                  SELECT points INTO balances_points
    ->                  FROM balances
    ->                  WHERE userid =users_userid
    ->                  ORDER BY timestamp_dump DESC
    ->                  LIMIT 1;
    ->                  SET done = alt_done;
    ->
    ->                  IF users_points != balances_points OR balances_points IS
 NULL THEN
    ->                          INSERT INTO balances (userid,points) VALUES (use
rs_userid,users_points);
    ->                  END IF;
    ->          END IF;
    ->  UNTIL done END REPEAT;
    ->  CLOSE cur1;
    -> END;
    -> //
Query OK, 0 rows affected (0.00 sec)

mysql> delimiter ;
mysql> INSERT INTO users(userid,points) VALUES(1,5),(2,10),(3,15);
Query OK, 3 rows affected (0.06 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> CALL pull_dump();
Query OK, 1 row affected (0.16 sec)

mysql> SELECT * FROM balances;
+--------+--------+---------------------+
| userid | points | timestamp_dump      |
+--------+--------+---------------------+
|      1 |      5 | 2008-07-18 15:39:38 |
|      2 |     10 | 2008-07-18 15:39:38 |
|      3 |     15 | 2008-07-18 15:39:38 |
+--------+--------+---------------------+
3 rows in set (0.00 sec)

mysql> UPDATE users
    -> SET points =20
    -> WHERE userid =3;
Query OK, 1 row affected (0.06 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> CALL pull_dump();
Query OK, 1 row affected (0.05 sec)

mysql> SELECT * FROM balances;
+--------+--------+---------------------+
| userid | points | timestamp_dump      |
+--------+--------+---------------------+
|      1 |      5 | 2008-07-18 15:39:38 |
|      2 |     10 | 2008-07-18 15:39:38 |
|      3 |     15 | 2008-07-18 15:39:38 |
|      3 |     20 | 2008-07-18 15:39:47 |
+--------+--------+---------------------+
4 rows in set (0.00 sec)

Test this on dev before using on production. I have no idea how fast
this will be...

-- 
Rob Wultsch
[EMAIL PROTECTED]

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

Reply via email to