views:

134

answers:

4

I need to track the completion of tasks for users.

I started by having a row called "first_login", "profile_complete", "other_thing_complete", etc...

I'd like to combine these all into one row "activity_state".

I just don't know how to properly store data in this field, and how to retrieve it.

What kind of field should it be, and how should I read/write to it?

+2  A: 

I'm not sure I fully understand the requirements - and I'm sure others here will have a better answer... but here is my go.

I assume you have a users table already.

What if you added an activity table and a users_activities table.

activity
-------------
id
name

users_activities
------------------
id
activity_id
user_id

So whenever a user completes an activity, you just add a record to the users_activities table.

someoneinomaha
That would defeat the purpose of using one row. :)
Kevin Brown
Ah... my bad. What's the reason for limiting it to one row?
someoneinomaha
I just thought it'd be cool to be able to do this for a bunch of activities by returning one value...
Kevin Brown
+1  A: 

If you only have a small number of "activities" that you'd like to track, you could have a table with a BOOL column for each activity you want to track.

activity
--------
INT user_id
BOOL first_login (default 0)
BOOL profile_complete (default 0)
BOOL other_thing_complete (default 0)

This could even be a part of your existing users table. Each column would default as 0/false; when an activity occurs, you simply update it to be true. For example:

UPDATE activity SET first_login = 1 WHERE user_id = 23
Dolph
What if I want to have these things stored in one row. Is it possible to use the one's place as "first_login", the ten's place as "profile_complete" etc?
Kevin Brown
The table above is four columns - one user would be represented by one row.
Dolph
A: 

You seem to be confused about the meaning of "row". It seems that you first created a table with the columns "first_login", "profile_complete", "other_thing_complete", etc. Each user would have their own row in that table, with the columns that you have specified.

You then seem to have determined that you want to store all of those data elements in a single column called "activity_state", presumably because you realized that you would have to update the table every time you identified a new task that you wanted to track.

If you're going to do this - store all of your data elements in a single column - you should store it as a serialized object.

That said, the method suggested by someoneinomaha is the best way to do this. It is completely extensible. You don't have to worry about parsing the field. Most importantly, with your method, you will have to perform full table scans any time you're searching the table on any of the values you are tracking.

coolgeek
Sorry, I was mixed up about rows/cols! So you're saying it would be a very bad idea to store all the "activity_state"s in a single serialized column?
Kevin Brown
In this case, yes. And in most cases, serializing is bad. However, there are situations where you don't have much of a choice, such as with storing user sessions between hits/visits.Also note that the boolean method described by dolph and Sid requires you to update the table definition every time you want to track a new task. This can essentially bring your system to a halt, given a sufficiently large table.
coolgeek
+1  A: 

Never store serialised objects, accessing them is slow (retrieval, unserializing) and searching is even harder.

If it's a relateively small number of tasks and they are boolean you could store them as bit masks (that may be the wrong term).

1 = first_login
2 = profile_complete
4 = other_thing_complete
8 = other_thing2_complete
16 = other_thing3_complete
...

Then the value 10 means profile_complete+other_thing2_complete. See http://dev.mysql.com/doc/refman/5.0/en/bit-functions.html or searching these.

mysql> select 10&2;
+------+
| 10&2 |
+------+
|    2 |
+------+
1 row in set (0.00 sec)

mysql> select 10&8;
+------+
| 10&8 |
+------+
|    8 |
+------+
1 row in set (0.01 sec)

mysql> select 10&1;
+------+
| 10&1 |
+------+
|    0 |
+------+
1 row in set (0.00 sec)

mysql> select 10&4;
+------+
| 10&4 |
+------+
|    0 |
+------+
1 row in set (0.00 sec)

mysql> select 10&16;
+-------+
| 10&16 |
+-------+
|     0 |
+-------+
1 row in set (0.00 sec)

As you can see only the first two queries returned a non-zero value, meaning that if you search for records where the field anded with 8 is non-zero you get all records where other_thing2_complete is complete etc. To set other_thing2_complete to true you simply add 8. To set other_thing2_complete to false you simply subtract 8.

Never done this myself, so you may run into problems I've not foreseen. But if all the activities you are keeping track of are boolean, then this should do the trick and allow for the best searching/updating.

Sid
This seems like more trouble than simply having a few extra fields for completed tasks...
Kevin Brown
Well this can easily be expanded with time, without adding more fields (which can affect any CMS or import utility which may be related to the project). If you need to later keep the status of a new action, just make 32 signify that field (as in the above example the highest used value was 16).But yes, more fields is the proper way to do this, see Dolph Mathews's answer.
Sid