If we look at the stackoverflow website we have votes. But the question is what is the bestway to store who has voted and who has not. Lets also simplify this even more and say that we can only vote Up, and we can only Remove the Up vote.
I was thinking having the table to be in such form
question - Id(INT) | userId(INT) | title(TEXT) | vote(INT) | ratedBy(TEXT)
Thre rest is self explanitory but ratedBy is a Comma Seperated Id values of the Users.
I was thinking to read the ratedBy and compare it with the userId of the current logged in User. If he dosent exist in the ratedBy he can vote Up, otherwise he can remove his vote. Which in turn will remove the value from ratedBy
I think to make another table "vote" is better. The relationship between users and votes is n to n, therefore a new table should be created. It should be something like this:
question id (int) | user id (int) | permanent (bool) | timestamp (datetime)
Permanent field can be used to make votes stay after a given time, as SO does.
Other fields may be added according to desired features.
As each row will take at least 16B, you can have up to 250M rows in the table before the table uses 4GB (fat32 limit if there is one archive per table, which is the case for MyISAM and InnoDB).
Also, as Matthew Scharley points out in a comment, don't load all votes at once into memory (as fetching all the table in a resultset). You can always use LIMIT clause to narrow your query results.
A new table:
Article ID | User ID | Rating
Where Article ID and User ID make up the composite key, and rating would be 1, indicating upvote, -1 for a downvote and 0 for a removed vote (or just remove the row).
I believe your design won't be able to scale for large numbers of voters.
The typical thing to do is to create to tables
Table 1: question - Id(INT) | userId(INT) | title(TEXT)
Table 2: question - ID(INT) | vote(INT) | ratedBy(TEXT)
Then you can count the votes with a query like this:
SELECT t1.question_Id, t1.userId, t1.title, t2.sum(vote)
FROM table1 t1
LEFT JOIN table2 t2 ON t1.question_id = t2.question_id
Related
I'm making a quiz game, and I want to make statistics of questions that has been answered wrong.
I have a database with 3 tables: Questions (all questions), Answers (the alternatives for each question), Games (stats about each game sessions).
Right now I'm thinking of two alternative ways to make a solution for this.
Save each question ID into an array, then save the array
into one record in MySQL at the end of the game.
Insert a new record with question ID into the table for each wrong
answer.
Which of these options would be the best approach to solve my problem? If I'm correctly, the last one will be easier when I'm gonna query the database to show questions that has been answered wrong. Any input or suggestions would be appreciated!
My advice is to save each answer given into a separate row, exactly for the reason you stated. Querying such a structure will be much easier than having multiple answers stored in a single row.
I would structure the tables as follows
Question
ID | Text | whatever else you may need
Answer
ID | QuestionID | Text | IsCorrect | whatever else you may need
Game
ID | User | StartTime | EndTime | whatever else you may need
Stats
ID | GameID | QuestionID | AnswerID | AnswerTime | whatever else you may need
This would need adjustments depending on the kind of analysis you're planning to do on it, but you get the idea.
Counting the number of times a wrong answers has been given would be as easy as
select t1.ID, count(*)
from Answer t1
join Stats t2
on t1.ID = t2.AnswerID
where t1.IsCorrect = 'N'
group by t1.ID
I am not sure I understood correctly your question. What follow is a possible solution for table statistics for answers if you want detailed information for each question / answer :
CREATE TABLE GAME_STATS (ID INT NOT NULL, ID_QUEST INT, ID_ANSWER INT, NUM INT);
ID is a sequence (you could omit this and make PK ID_QUEST, ID_ANSWER)
ID_QUEST refers to ID of table Questions
ID_ANSWER refers to ID of table Answers.
NUM is the number of times that answer has been selected.
You could consider to prepopulate the table for all possibile questions / answers (NUM = 0).
At the end of every answer, you should update the NUM (+1) for ID_QUEST, ID_ANSWERS.
If you want register statistics for each session / user, you should add appropriate columns (ex. id_session, and/or user_id).
In this case you can't prepopulate table. And you can register only wrong answers.
I have a MySQL table, people, that looks like this:
id | object_id | name | sex | published
----------------------------------------------
1 | 1 | fred | male | [timestamp]
2 | 2 | john | male | [timestamp]
The reason I have two ids is that in my CRUD app the user might edit an existing object, in which case it becomes a draft, so that I have two rows (the draft record and the already-existing record) with the same object_id, something like this:
id | object_id | name | sex | published
----------------------------------------------
2 | 2 | john | male | [timestamp]
3 | 2 | john | female | NULL
This allows me to keep track of records' drafts and publication status. When the row with id of 3 is published, its published field will be stamped and the already published row deleted.
Each person also has a job history, so I have a table history:
id | person_object_id | job
----------------------------------
1 | 2 | dev
2 | 2 | accountant
This is John's job history. I refer to John's object_id in the person_object_id field, because if I refered to his id I'd risk delinking the two tables if I deleted one of the John rows as in my example above.
So my question is: is it not inefficient to refer to a table, as I do above, using a non-primary key (object_id instead of id)? How can I refer to a primary key when I require a non-unique id to keep track of drafts/published rows?
It looks like you want to keep versions of your data and you've come across the age-old problem of how to maintain foreign key pointers to versioned data. The solution is actually easy and it turns out that it is a special case of second normal form.
Take the following employee data:
EmpNo FirstName LastName Birthdate HireDate Payrate DeptNo
Now you are tasked with maintaining versions of the data as it changes. You could then add a date field which shows when the data changed:
EmpNo EffDate FirstName LastName Birthdate HireDate Payrate DeptNo
The Effective Date field shows the date each particular row took effect.
But the problem is that EmpNo, which was a perfect primary key for the table, can no longer serve that purpose. Now there can be many entries for each employee and, unless we want to assign a new employee number every time an employee's data is updated, we have to find another key field or fields.
One obvious solution is to make the combination of EmpNo and the new EffDate field be the primary key.
Ok, that solves the PK problem, but now what about any foreign keys in other tables that refer to specific employees? Can we add the EffDate field to those tables, also?
Well, sure, we can. But that means that the foreign keys, instead of referring to one specific employee, are now referring to one specific version of one specific employee. Not, as they say, nominal.
Many schemes have been implemented to solve this problem (see the Wikipedia entry for "Slowly Changing Dimension" for a list of a few of the more popular).
Here's a simple solution that allows you to version your data and leave foreign key references alone.
First, we realize that not all data is ever going to change and so will never be updated. In our example tuple, this static data is EmpNo, FirstName, Birthdate, HireDate. The data that is liable to change then, is LastName, Payrate, DeptNo.
But this means that the static data, like FirstName is dependent on EmpNo -- the original PK. Changeable or dynamic data, like LastName (which can change due to marriage or adoption) is dependent on EmpNo and EffDate. Our tuple is no longer in second normal form!
So we normalize. We know how to do this, right? With our eyes closed. The point is, when we are finished, we have a main entity table with one and only one row for each entity definition. All the foreign keys can refer to this table to the one specific employee -- the same as when we've normalized for any other reason. But now we also have a version table with all the data that is liable to change from time to time.
Now we have two tuples (at least two -- there could have been other normalization processes performed) to represent our employee entity.
EmpNo(PK) FirstName Birthdate HireDate
===== ========= ========== ==========
1001 Fred 1990-01-01 2010-01-01
EmpNo(PK) EffDate(PK) LastName Payrate DeptNo
===== ======== ======== ======= ======
1001 2010-01-01 Smith 15.00 Shipping
1001 2010-07-01 Smith 16.00 IT
The query to reconstruct the original tuple with all the versioned data is simple:
select e.EmpNo, e.FirstName, v.LastName, e.Birthdate, e.Hiredate, v.Payrate, v.DeptNo
from Employees e
join Emp_Versions v
on v.EmpNo = e.EmpNo;
The query to reconstruct the original tuple with only the most current data is not terribly complicated:
select e.EmpNo, e.FirstName, v.LastName, e.Birthdate, e.Hiredate, v.Payrate, v.DeptNo
from Employees e
join Emp_Versions v
on v.EmpNo = e.EmpNo
and v.EffDate =(
select Max( EffDate )
from Emp_Versions
where EmpNo = v.EmpNo );
Don't let the subquery scare you. A careful examination shows that it locates the desired version row with an index seek instead of the scan that most other methods will generate. Try it -- it's fast (though, of course, mileage may vary across different DBMSs).
But here's where it gets really good. Suppose you wanted to see what the data looked like on a particular date. What would that query look like? Just take the query above and make a small addition:
select e.EmpNo, e.FirstName, v.LastName, e.Birthdate, e.Hiredate, v.Payrate, v.DeptNo
from Employees e
join Emp_Versions v
on v.EmpNo = e.EmpNo
and v.EffDate =(
select Max( EffDate )
from Emp_Versions
where EmpNo = v.EmpNo
and EffDate <= :DateOfInterest ); --> Just this difference
That last line makes it possible to "go back in time" to see what the data looked like at any specific time in the past. And, if DateOfInterest is the current system time, it returns the current data. This means that the query to see current data and the query to see past data are, in fact, the same query.
It doesn't really matter as long as you have an index on that column (not-unique index). Than it would be almost as fast
I want to create an up vote and down vote system for my website where a unique user can vote up/down for one post and next time he only allow to opposite to get off from database and after that he again can up or down vote.
In this case I have:
users table :
id
name
debates table :
id
post
upvotes table:
id
user_id
debate_id
and similarly downvote table:
id
user_id
debate_id
Is that a good way to manage and track up vote and down vote concept?
I think, you can use a single table to track the votes and the structure could be something like this
Table : votes
id | user_id | debate_id | vote
Here, vote field could be tinyInt with defauld null.
And, in vote field, you just keep two different values depending on the vote type, for example, if a user up votes then insert a value of 1 in the vote field and for down vote, insert the value of 0. So, your table may look something like this
id | user_id | debate_id| vote
1 | 10 | 4 | 1 <-- up
2 | 11 | 4 | 0 <-- down
3 | 12 | 4 | 1 <-- up
In this case, two users with id = 10 and id = 12 up voted the post whose debate_id = 4 and another user with user_id = 11 down voted on the same post.
IN this case, you may find out how many up or down votes a post got by counting the vote field's value, for example, you may count for up votes for debate_id = 4 using something like this
$count = Debate::where('debate_id', '=', 4)->where('vote', '=', 1)->count();
Also, you may use something Query Scope, this is just an idea and it's not possible to make an answer which covers everything in this scope. You should start using some basic idea and if you stuck at a certain point, then you may ask specific questions with your code.
Also, I would like to mention that, if you find a user id in the votes table with a certain debate_id then this user has voted on this post and to find out the vote type, just check the vote field 1 or 0.
I would prefer to only have one table containing the votes, this could be done with an extra column such as is_downvote int(1).
It seems that you havn't tried much which is always a negative. For this scenario the Laravel Eloquent Documentation should be plenty to figure this out.
I would of written this as a comment but it's pretty lengthy now.
The question is not new in any way but it has a small twist to it.
My webpage is a membership page where users places bets. My idea is to create a new table for the users(with a naming convention like TABLE userBet+$userid) bets. User login information is already handled, my goal is now to save the bets of the user to a new table. A table which is created when users register. This will hopefully make score counting easier. Am I right or wrong? Could this be done in a better way? (Everything is done in PHP MySQL)
User registers -> Table for bets get created
"CREATE Table $userID ,id_bet, games, result, points"
And then matching this table against the correct result?
So again my questions: Is this a good way to do it? Is creating a table with the userID a smart thing to do?
EDIT
The bets is always 40 matches, which makes the tables Huge with columns and rows.
Should I make 40 Tables, one for each games instead? and put all users in there?
Am I right or wrong?
You are wrong. Dynamically altering your database schema will only make it harder to work with. There's no advantage you gain from doing so. You can do the same things by storing all bets within the same table, adding a column userid.
Posting as an answer due to author's request : )
Suggested database schema:
table matches:
id | name |
---------------
1 | A vs B |
table user_bets
id | user_id | match_id | points | result |
-------------------------------------------
1 | X | 1 | Y | Z |
Where match_id is related on matches.id
user_id = user.id
user_bets is only one table, containing all the info. No need of separate tables, as it was clear from the comments it's considered bad practice to alter the db schema via user input.
I'm trying to build a php ranking system where users can rank an image on a scale of 1-5.
Depending on how an image is ranked decides what its place on the leaderboard (rank number) would be. The rank should change depending on the different ratings it receives from users.
An example of this is the ranking system here. http://www.newgrounds.com/portal/view/601966 (Right hand side, lower down the page.)
I'm just looking for any information which would help me achieve this.
Thanks.
Create a table called votes and tie it to your images table:
VOTES:
vote_id INT(11) PK
user_id INT(11)
image_id INT(11)
score TINYINT(1)
Here are some things you're going to need to know:
You need a database. In your database you need to store each of the images you're ranking, do this in a table called "images". In this table you will give each image an "auto-incrementing" primary key. (this means that for each new row you add to the database the primary key will AUTOMATICALLY be +1 from the row before). This means that each image has a UNIQUE row number next to it - identifying that specific row. Call this column id. (we will reference it in other tables in the column image_id).
Next you need a table called "votes". In this table you can store all sorts of information you might need, but simply all you'll need to store is the unique image number from the "images" table and the value of the vote that someone has cast. You'll end up with something like this:
image_id | vote_value
1 | 3
2 | 5
1 | 3
4 | 1
4 | 3
Now you can query this information to get your leaderboard. The query might look something like this:
SELECT image_id, SUM(vote_value) AS rank FROM votes GROUP BY image_id ORDER_BY rank;
That will give you a list of "image_id"s ordered by their rank (i.e. the total of all the votes).
Then you can go back to your images table and get the information for that image out of that table.
SELECT name, url FROM images WHERE id=#image_id we got above#;
Hope this helps you. :) If you get stuck come back and ask again.