I have a users table that has the following fields: userid, phone, and address. Since this is user data, I'm letting the user change them whenever he wants. Problem is I'd like to keep track of those changes and preserve the old data too. Here's some of the ideas I considered:
appending the new data to the old data and using a separator like a pipe. When retrieving the field, I would check for the existence of that separator and if exists, get the chars after it as the new data. (feels cumbersome and doesn't feel right)
setting up a different changes table with the following fields: userid, fieldname, fieldcontent. When/if a user changes data (any data), I would log the event in this separate table under the user's userid, and the name/id of the field and the old content of the field, then I can now overwrite his old data in users with the new. If I want to find all changes made by this user, I would search the changes table by his userid. Problem with this is that I'm mixing all data changes (of all fields) into one table and so the fieldcontent field in changes has to be text to accommodate the varying field types. This still seems better than the first idea, but still not sure if I'm doing the right thing.
What other ideas are there or known best practices to keep old data?
Thanks in advance
Whatever you do don't do the first one.
The changes table is a better approach. It's also called an audit or history table. I wouldn't do a history of key-value pairs however. Instead do a history per relevant table. You can do this in application code or via database triggers. Basically whenever an insert, update or delete happens you record which happened and what data was changed.
Table user:
id
username
email address
phone
address
Table user_history:
id
change_type (I, U or D for insert, update or delete)
user_id (FK user.id)
email address
phone
address
date/time of change
optionally, also store who changed the record
A very simple way that we have used to track such changes is this:
users_history`
userid
changenumber smallint not null
changedate datetime not null
changeaddr varchar(32) not null
phone NULL,
address NULL
primary key on (userid, linenumber)
Each time you INSERT or UPDATE a record in the users table, simply INSERT a new record in the users_history table. changenumber starts at 1 and increments from there. changedate and changeaddr could be used to track when and where.
If a field value has not changed, feel free to put NULL in the respective users_history table field.
At the end of the day, your app does not need to change or store bulky history data in the users table, but you have all if it at your fingertips.
Edit:
This does preserve the old data. See the following example where the user started with a given address and phone, and then 4 days later updated the address, and 5 days later updated the phone. You have everything.
Current users record:
100 | 234-567-8901 | 123 Sesame Street
Sample History Table
100 | 1 | 2009-10-01 12:00 | 123-456-7890 | 555 Johnson Street
100 | 2 | 2009-10-05 13:00 | NULL | 123 Sesame Street
100 | 3 | 2009-10-10 15:00 | 234-567-8901 | NULL
The simplest way to implement this will be have another table just for history purpose, a snapshot. You don't need to mirror all the fields, just
change_id // row id (just for easy management later on if you need to delete specific row, otherwise its not really necessary)
user_id // Original user id
change_time // time of change
data // serialized data before change.
Related
I am trying to create a pivot table to help keep track of "challenges" in my applications. Basically I have a challenge_task pivot table that creates a relationship between a challenge and a task. When a user that is in a challenge completes a task I want to be able to tell so I can track a user's progress. How is the best way to store multiple users completing a task on a challenge?
I was thinking in the pivot table adding a json column called user_completed to handle this and store the user_id for every user that completes the task for a challenge.
So challenge_task would look like
challenge_id | task_id | user_completed
Is this a good way? Or is there anything that fits this better?
I'd recommend a database structure something like this:
challenge: challenge_id | other data
task: task_id | other data
user: user_id | other data
challenge_task: challenge_task_id | challenge_id | task_id
| possibly more data (such as deadline for completion)
challenge_task_users: challenge_task_id | user_id
| possibly more data (such as status: accepted, in progress, completed)
I dont recommend Json if you want to index your data, because Json can not be indexed.
I think you should make a pivot table between the users and the tasks too, and create the neccesary relations.
I wouldn't recommend you inserting multiple values in one database column.
Note: This is my opinion. Just sharing the way I use it.
A table called tasks_settings which has the task settings.
I find this way flexible because I can always edit the title, description, and reward easily. I can also add 2 more fields here which are valid_till and valid_for. So you can make it expire after a period of time and only for a special rank like staff or all users.
Another table called users_tasks
This one controls the users. Whether they have completed the task or not. This could also achieve what you are looking for.
id | challenge_id | task_id | username | user_completed
I hope this has helped you!
Hi I am in the process of making a website that includes a user registration system for my final year of high school major project. The website stores driving logs for learner drivers. I'm kind of confused as to how I should desgin the database. I have a users table which stores the personal information of each user of the site. However, I would like the user to be able to insert information into another table which would be their "logbook" and this to be displayed on the my account page. Do I need to create a table within the database for each user or is there a way of connecting the tables so that i do not have to.
You do not need to create a table for each user. Instead, add a column in your "logbook" table which will contain and refer to the "id" of the user it's tracking. This will likely be the primary key of your "users" table. Then, to get the logs for a specific user, you would query the logbook table for rows only with at specific user ID.
Furthermore, in a more sophisticated setup, you can add constraints to link the two columns. See http://dev.mysql.com/doc/refman/5.6/en/create-table-foreign-keys.html
U will not need table for each user...
Just one table will help u out...
The other table where u want to store user logs should only have reference to user I'd that u have created in users table..
Primary and foreign key - relational table concepts
For eg:
Let ur user has
1.userid
2.first name
3.lastname.... And other colums
Then ur userlogbook wala table should have
1.logid
2.userid
3.other columns...
Hope u got it..
You only need 1 table for the users, each user will be a row inside that table.
As you see in this table below, you need a way to be able to track which logbook record belongs to which user. That's done by storing the users ID (or any unique identifier, usually the primary key) to know exactly which user created the record.
-------------- -----------------
| User Table | | Logbook Table |
------------------------- -----------------------------------
| id | name | ...etc... | | id | user_id | date | ...etc... |
------------------------- -----------------------------------
| |
|_____________________________________|
I don't know how your system works, but I assume you know when a user is logged in right? Probably store their id in a session yeah? Well when you're inserting the logbook record, all you need to do is parse through their user ID in addition:
INSERT INTO logbook (id, user_id, .....) VALUES (NULL, $THE_USER_ID_FROM_SESSION, .....)
The above is pseudo code, you'd need to sanitize the input and actually assign the user id to a variable.
Now for fetching the user-specific information, all you need to do is add a simple WHERE clause:
SELECT id,column_1,column_2,... FROM logbook WHERE `user_id` = $THE_USER_ID_FROM_SESSION
The above is pseudo code, you'd need to sanitize the input and actually assign the user id to a variable.
There are a few questions I have, how much reading/writing are you going to be doing to the table? How are your tables set up?
I'm currently having a single activity table that references other tables depending on the type of activity.
id | type | user_id | source_id | misc_id | date
The type column tells me what kind of activity the user has performed (follow, liked, befriend, status etc.) and the source id contains the table id relative to the type of action.
This is working well for a user activity stream, but the only problem is, I can't figure out what to do about rows that no longer exist in the relative tables?
E.g. a user creates a status and then deletes it, or a user becomes friends with somebody that is later deleted from the database.
If the activity was relative to a single type, then I would be able to add a foreign key constraint, which would remove the row; but as it's relative to different tables, how else could I go about doing this?
You will have to take either of these approaches.
When the user deletes, just do the soft delete on the backend by marking them as deleted instead of hard deleting from the table. You will have to introduce a new column "delete_flag" in this approach.
Archive the tables and move the records to a different table when deleted. But this would be complex coding wise as well as the performance might not be as expected.
Here are my thoughts.
If users can delete something, you can record this also in your activity table.
I you want to hide that activity, you can add ReversedOn field and update it with the relevant date. Then you will just have to filter out activities that don't exist.
If that does not cause any user experience problems, then you can just let it be.
Im going to develop Stock maintaining system using php+mysql. which will runs on server machine, so many users can update stock data. (in/out)
Im currently working on this system. I have following problems.
User A opens record “A”. ex- val=10
User B opens record “A”. ex - val=10
User A saves changes to record “A”. ex - val=10+2=12 (add 3 items, then stock should be 12)
User B saves changes to record “A”. ex - here i need to get record "A" value AS = 12, then B update val=12+3=15. (then add 3 items final stock will be 15)
In this example, User A’s changes are lost – replaced by User B’s changes.
I know mysql Innodb facilitate row level locking. My question is ,
is innodb engine do concurrent control ; and is this enough to (Innodb) to avoid "lost update" problem. or need to do extra coding to avoid this problem.
Is this enough please tell me how innodb works with my previous example. (lost update)
(sorry for my bad english)
thanks
InnoDB allows concurrent access, so User A and User B could definitely be handling the same data. User A will update the row based on his/her data, then User B can do the same -- ultimately resulting in User A's loss of data.
You should consider an alternative, if every update is vital to keep. For example, if both users are updating a blog article, you could make a new table that holds all these edits. Both user's edits would be preserved, despite when they retrieved the article content. When the article is retrieved, you can check when the most recent edit occurred and retrieve that instead.
Look, there's something called "versioning".
The idea is simple:
When a user opens a record, he also gets the version number.
When he saves changes to that record, at the sql level, the update is conditional, meaning that the update will happen ONLY if the current version is the same. This update also increases the version by one.
This way ensures you're not writing to a "stale" copy of your record.
Hope it's clear.
You could also implement some polling to the server, keep a record of the last update of the row and if it changes where if user B updates the record before A then you can notify user A that the record has been updated and that his changes wont take effect or you could update the values dynamically.
You can use two tables for this purpose. First - StockItems with item name, id, and count. Second - StockActivities with item id and operation amount.
To add or remove items from stock you need to insert records to the second table StockActivities, with item id and quantity that is added / removed.
item id:1, qnt: +10
item id:1, qnt: +1
item id:10, qnt: -2
Field count of StockItems table should be "read only" for users and should be calculated based on StockActivities table.
For example, you can create after insert trigger for StockActivities table that will update count field of added / removed stock item.
Judging by comments left, I think it prudent to respond with some pointers I have come across, in case someone needs to.
If you only want to update a value by an offset, you can do this quite easily and atomically. Assume the following data:
+----+--------+-------+
| id | name | price |
+----+--------+-------+
| 1 | Foo | 49 |
| 2 | Bar | 532 |
| 3 | Foobar | 24 |
+----+--------+-------+
We can now run the following queries to add one to the price:
select id, price from prices where name like "Foo";
// Later in the application
update prices set price=50 where id=1;
This is the non-concurrent/non-atomic way to do this, assuming that there is no changes or fetches in between the two queries. A more atomic way to do this, is the following.
select id, price from prices where name like "Foo";
// Later in the application
update prices set price=price+1 where id=1;
Here, this query allows us to increment the price in one query, eliminating the ability for others to come and update between two queries.
Additionally, there are methods of updating data safely, where the nature of the update is not a simple addition or subtraction. Let's say, here, that we have the following data:
+----+----------+---------------------+
| id | job_name | last_run |
+----+----------+---------------------+
| 1 | foo_job | 2016-07-13 00:00:00 |
| 2 | bar_job | 2016-07-14 00:00:00 |
+----+----------+---------------------+
In this case, we have multiple different clients, where all clients can do any job. We then need a way to dispatch work to one client, and only one client.
We can either use a transaction, where we will error out if the record has been updated or we can use a technique called CAS, or Compare and Swap.
Here's how we do this in MySQL:
update jobs set last_run=NOW() where id=1 and last_run='2016-07-13 00:00:00'
Then, in the data returned from mysql, we can tell the number of rows affected. If we have affected a row, then we have successfully updated it, and the job is ours. If there were no rows updated, then another machine has updated it, claiming the job there.
This works because any update from our application will cause the column to change, and since the column's value is a condition for completing the updated, it will avoid concurrent changes, allowing the application to decide what occurs next.
This is a followup to a question I posted a few days ago.
basically, I have a site with six links. In order to access the site, users must log in using LDAP authentication. When they do this, I grab some of their account credentials (username, firstname, lastname), and store it in a PHP $_SESSION variable.
That works; the user can log in, and the session data is being stored successfully.
Now, I want to set up a way to track which links have been clicked by what users. Basically just store a time stamp in the database of when they clicked the link. I want to be able to see who has (or has not) clicked each link, and when.
Can I do this in a single table / would that be a bad idea? I was thinking setting up the table like this:
TABLE (each bullet indicative of a column)
auto-incrementing ID
user account name: abc1234
user account first name: John
link 1: Last Accessed 5/2/2012 at 4:15PM
link 2: NULL
link 3: NULL
link 4: Last Accessed 5/1/2012 at 2:20PM
link 5: NULL
link 6: NULL
basically the above would say that "John" had only clicked the first and 4th links. The rest are null because he has never accessed them. If he were to click #1 again, it would overwrite with the more recent date/time.
Can I do this in a single table? or will that create complications? I feel like the thing I will have the hardest time with is checking to see if the user is already in the database before adding the info (ie so that if John logs in a second time, a whole new row isn't created for him)
Thanks for any help!
That would be a bad idea. What if you wanted to have a seventh link? What if the user format would change?
This solution requires 3 tables:
Users - contains user data (And a user ID).
Links - contains link data (And a link ID).
Clicks - many-to-many relationship between users and links.
That third table would look like this:
user_id | link_id | timestamp
-----------------------------
1 | 2 | ...
2 | 2 | ...
1 | 3 | ...
............
why not just have
increment_ID
Account_ID
Link_URL
Timestamp
Then just insert a new record for each click. You also don't need to manage links since you'll store the entire URL path