I am developing a website that is a kind of game. The users' progress is saved in a MySQL-database.
I want to go about this by having a table saves with a column save (ID) and a column progress, where progress is of datatype text. When the user starts out, progress is set to (e.g.) '0'. If he proceeds to level 1, progress is set to '0#1', level two makes it '0#1#2'. The order of levels is free and I want to save it. So progress could be '0#4#2#15' and so on.
Is this a good way to do this? I have no experience with SQL and I don't want to do something incredibly stupid. I've read so much confusing info about tables, foreign keys and whatnot...
I want to thank you for your time reading this and I'm looking forward to answers.
Ryan
Answer to your Question 1
I would not approach your problem this way. I would create 3 tables: a Levels table (primary key of 'levelKey'), a Users table (primary key of 'userKey') and a User_Levels table with a composite key of 'levelKey' and 'userKey'. When a user completes a level, just insert into the User_Levels table. Then to see if a user has completed a level is a simple select:
SELECT 'a' FROM User_Levels WHERE userKey = ? AND levelKey = ?
If the number of rows is > 0, the user has completed the level
As for Question 2, I'd say the amount of queries is not the problem. After all, you are writing data to the database, not accessing it. Personally, I would send a "save" to the database, whenever a user actually completes a level.
watcher has posted a good approach for splitting up the levels and users into different tables. The sequence of progress can be seen from the order the progress gets logged into the User_Levels, so no need to store something like 1#3#4#9
You will probably want to send the save in the background with ajax, so you don't interrupt the game play. Look into jQuery's $.post method, for example. Or if your game is in flash, you can use a URLRequest.
About your first question, if the game / levels are non-linear personally I would take a different approach; I would simply add a table which contains a column for the user ID and a column for the level completed. So if user 1 has completed levels 0, 4 and 7, my table would have 3 rows:
UID levels_completed
1 0
1 4
1 7
About your other questions, you can use javascript events and ajax to detect the closing of the page but I would not rely on that; I would just run the queries whenever needed. And if your session is destroyed, you are already too late...
Related
I am working on social networking site, which includes the creation of media content and also records the interaction of users with the created content.
Background of issue - approach used currently
There is a page called news-feed, which displays the content and activity done with the content by the users they are following on site.
Display order of the content changes with more and more user interactions(eg. if there are more number of comments on a post, its likely to be shown on top of the one with lesser number of comments. However, number of comments is just one of the attributes used to rank the post).
I am using mysql(innodb) database to store the data as follows:
activity_master : activities allowed to be part of news feed(post, comment etc)
activity_set : for aggregation of activities on the same object
activity_feed: details of actual activity
Detailed ER Diagram is at the end of question
Scenario
A user(with 1000 followers) posts something, which initiates an async call to the procedure to insert the relevant entries(1000 rows for 1000 followers) in above mentioned tables for all followers.
Some followers started commenting(activity allowed to be part of news feed) before the above call is completed which initiates another call to the same procedure to insert entries(x total number of their own followers) of this activity for their particular set of followers. (e.g User B commented on this post)
All the insert requests(which seems way too many) will have to be processed in queue by innodb engine
Questions
Is there a better and efficient way to do this? (I definitely think there would be one)
How many insert requests can innodb handle in its default configuration?
How to avoid deadlock (or resource congestion at database end) in this case
Or is there any other type of database best suited in this case
Thanks for showing your interest by reading the description, any sort of help in this regard is much appreciated and let me know if any further details are required, thanks in advance!
ER Diagram of tables (not reputed enough to embed the image directly :( )
A rule of thumb: "Don't queue it, just do it".
Inserting 1000 rows is likely to be untenable. Tomorrow, it will be 10000.
Can't you do the processing on the select side instead of the insert side?
My app's functionality is like Tinder. I will go through the work flow.
App loads 10 Hunts (like tinder profiles)
User accepts or rejects it
Once user accepts or reject, hunt is removed (marked as seen so that it doesnt come back again )
When Hunts count become 2 , app loads next 10 hunts. ( This is not second page as seen hunts are already removed )
Here is the tricky part. When it queries database again, the hunt would have the 2 hunts which user hasnt yet accepted or rejected. To avoid duplication I avoid first 2 hunts from the response. But problem occurs if the query is run after one more accept or reject. I would remove 2 hunts expecting normal behavior but this would remove eliminate one hunt which is not a duplicate.
What would a best solution would be to get all the hunts which comes after a certain id.I can use WHERE NOT ID IN by passing the ids. But I would like to know if there is a better solution as I see this would be a pretty common scenario .
I hope I made myself very clear.
The solutions which I have thought of but not really liked
Pass ids of the 2 hunts back and exclude them in the results
Remove duplicates from hunts once I receive response back in my app
All suggestions are welcome. I m using Rails so active record solutions are also welcome.
It may be wise to add a status column with values 0,1,2 for unseen, rejected, accepted.
Then, when your user accepts / rejects each item, update that column.
To get the oldest (lowest-id-value) chunk of items for a user, use something like this.
WHERE user=whatever AND status=0
ORDER BY id
LIMIT 10
Build an index on (user, status, id), and MySQL can optimize this query.
Which one would be better (performance wise and maintenance), a database which creates table dynamically or just adding rows dynamically?
Suppose I am building a project in which I let users to register. Say I have a table which store only basic personal infos, like name, dob, Date of joining, address, phone, etc. Say 10 columns.
Now is the tricky part.
Scene 1: Creating multiple tables
When a user complete registration, a message table is created. So each table is created for each users. The rows of each message table varies for each user.
In the same way there is a cart table for each user like the message table.
For this scene 1, 2 tables are created with every registration.
Scene 2: Adding Rows
The scenario is same here as well, but in this case I have 2 tables for message and cart. Rows are added only when there is an activity.
Note:
You must assume that the number of users is more than 2000 and expect 50+ users to be active all the time. Which means the message and cart tables are always busy for both the cases. Like there is always a query for update, add, delete, insert, select etc. simultaneously.
Also which scene will consume more disk space.
While writing this, it make me wonder what technique would Facebook and others use. If they use the Scene 2 style (all users (billions) use the same big long message table)... Just wondering
Databases has some basic rules defined for Database Design called
"Database Normalization", These basic rules allow us eliminating
redundant data.
1st Normal Form
Store One piece of information in only One Column, A column should store only One piece of information.
2ns Normal Form
A Table should have only the columns that are related to each other. All the related columns should be in One table.
Now if you look at your advised design, A Separate Table for each USER
will split SAME information/Columns about all the user in 1000's of
tables. Which violates the 2nd Normal Form.
You need to Create One Table and put all the related Columns in that
one table for all the users. and you can make use of normal t-sql to
query your data but if you have a table for each user my guess is your
every query that you execute from your application will be built
dynamically and for every query you will be using dynamic sql. which
is one of the Sql Devils and you want to avoid using it whenever
possible.
My suggestion would be read more about Database Design. Once you have
some basic understanding of database design. Draw it on a piece of
paper and see if it provides you everything that your business
requires / expects from this application , Spend sometime on it now it
will save you a lot of pain later.
I am creating a new DB in MySQL for an application and wondered if anyone could provide some advice on the following set up. I'll try and simplify things as best as I can.
This DB is designed to store alerts which are related to specific items created by a user. In turn there is the need to store notes related to the items and/or alerts. At first I considered the following structure...
USERS table - to store basic app user info (e.g. user_id. name, email) - this is the only bit I'm fairly certain does not need to be changed
ITEMS table: contains info on particular item (4 fields or so). Contains user_id to indicate which user created/owns this item
ALERTS table: contains info on the alert, item_id to indicate which item the alert is related to, contains user_id to indicate which user created alert
NOTES table: contains note info, user_id of note owner, item_id if associated with an item, alert_id if associated with alert
Relationships:
An item does not always have an an alert associated with it
An item or alert does not always have a note associated with it
An alert is always associated with an item. More than one alert can be associated with the same item.
A note is always associated with an item or alert. More than one note can be associated with the same item or alert.
Once first created item info is unlikely to be updated by a user.
For arguments sake let's say that each user will create an average of 10 items, each item will have an average of 2 alerts associated with it. There will be an average of 2 notes per item/alert.
Very common queries that will be run:
1) Return all items created by a particular user with any associated alerts and notes. Given a user_id this query would span 3 tables
2) Checking each day for alerts that need to be sent to a user's email address. WHERE alert date==today, return user's email address, item name and any associated notes. This would require a query spanning 4 tables which is why I'm wondering if I need to take a different approach...
Option 1) one table to cover items, alerts and notes. user_id owner for each row. Every time you add a note to an item or alert you are repeating the alert and/or item info. Seems a bit wasteful but item and alert info won't be large.
Option 2) I don't foresee the need to query notes (famous last words?) so how about serializing note data so multiple notes are stored in one row in either the item or alert table (or just a combined alert/item table)
Option 3) Anything else you can think of? I'm asking this question as each option I've considered doesn't feel quite right.
I appreciate this is currently a small project and so performance shouldn't be of great concern and I should just go with the 4 tables. It's more that my common queries will end up being relatively complex that makes me think I need to re-evaluate the structure.
I would say that the common wisdom is to normalize to start and denormalize only when performance data suggest that it's necessary.
Make sure that your tables are indexed properly, with foreign key relationships for JOINs.
If you think you'll end up with a lot of data, this might be a good time to think about a partitioning strategy. Partitioning your fast-growing tables by time would be a good first step.
Four tables is not complex. I commonly write report queries that hit 15 or more tables in a database structure that has hundreds of tables (most with millions of records) and I wouldn't even say our dbs are anything more than medium sized (a typical db in our system might have around 200 gigs of data, so not large at all as databases go). Because they are properly indexed, they still run fast unless I am doing very complex calculations. Normalize, don't even consider denormalizing until you are an experienced database designer who knows better than to worry about the number of tables.
I need to save a list of user ids who viewed a page, streamed a song and / or downloaded it. What I do with the list is add to it and show it. I don't really need to save more info than that, and I came up with two solutions. Which one is better, or is there an even better solution I missed:
The KISS solution - 1 table with the primary key the song id and a text field for each of the three interactions above (view, download, stream) in which there will be a comma separated list of user ids. Adding to it will be just a concatenation operation.
The "best practice" solution - Have 3 tables with the primary key the song id and a field of user id that did the interaction. Each row has one user id and I could add stuff like date and other stuff.
One thing that makes me lean towards options 2 is that it may be easier to check whether the user has already voted on a song?
tl;dr version - Is it better to use a text field to save arrays as comma separated values, or have each item in the array in a separate table row.
Definitely the 2nd:
You'll be able to scale your application as it grows
It will be less programming language dependent
You'll be able to make queries faster and cleaner
It will be less painful for any other programmer coding / debugging your application later
Additionally, I'd add a new table called "operations" with their ID, so you can add different operations if you need later, storing the operation ID instead of a string on each row ("view", "download", "stream").
It's definitely better to have each item in a separate row. Manipulating text fields has performance disadvantages by itself. But if ever you want to find out which songs user 1234 has viewed/listened to/etc., you'd have to do something like
SELECT * FROM songactions WHERE userlist LIKE '%,1234,%' OR userlist LIKE '1234,%' OR userlist LIKE '%,1234' OR userlist='1234';
It'd be just horribly, horribly painful.