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]