Hello I have voting system on my website but I would like to disable multiple voting from users. If the user votes he/she cannot vote again. Here is one solution which I have but is not good imo.
I created a column in users table: users_avoted where I would store the IDs of articles which he/she voted like tihs:
1|2|5|6
From this I would be able to check whether the user had already voted. But the thing is there will be a lot of articles so VARCHAR(255) will not be enough to store all of those IDs. Any other solutions?
Thanks
Don't denormalize it like that. Just have a two column table, userVotes, with a composite Key for UserID and ArticleID.
example data:
userid | articleid
1 1
1 2
1 67
2 1
This is better than something like
userID | articleID
1 1,2,67
Which is what it looks like you are suggesting. Please don't do this, and keep it normalized unless you want to do gymnastics every time you query. To elaborate, implementing what you suggested would defeat the purpose of using a RDBMS, in order to 'cheat' on one specific query.
You could instead make a table like:
users_avoted {
article_id,
user_id,
answer_id
}
Make a unique index out of article_id and user_id. That would only allow a single record to be input for that.
So, say they choose option 5 on article 2, and they are user 1.
Now, when you attempt to insert into the table:
INSERT INTO users_avoted VALUES (2, 1, 5)
You can't because the user already voted.
The benefit of this approach is that you don't have to check if they voted already first.
Store the votes in a table layed out like: id,username,voteid,vote for example.
Then when the user votes, check that table for matching rows.
For example:
if ($username == $dbusername && $voteid == $dbvoteid){
$disallowed = true;
}
Related
I have a table with data relating to a user, and two important columns:
refer_count, which is updated when a new entry is made in the table with the referred_by column set to that users user_id, and referred_by which is the user_id of the of the user that referred them.
I want to select the users from the table that have the highest number of referrals after a certain date.
For example:
If there are 3 users, one of which referred the other 2 (lets say users 2 and 3), however user 2 was referred on the 2/12/14, whereas user 3 was referred on the 3/1/15.
If the cutoff is 1/12/14, then user 1 is returned with refer_count set to 2, but if the cutoff is after 2/12/14, then user 1 is returned with refer_count set to 1.
I've been thinking of how to do this, but I can't think of a way that would work. Is there a way?
This is via MySQL.
EDIT: I think I may need to provide for information.
The date registered (register_date) is used as the refer date. I need the refer_count to be updated with the number of users referred after the cutoff, however I need to get the actual user. This is for a 'top referrers' table. I can't figure out why I'm having so much trouble thinking of a way to do this.
SELECT user_id FROM usertable WHERE (referal_date BETWEEN '2014-12-2' AND CURDATE())ORDER BY refer_count DESC;
That's the rough idea.
You should look into normalizing your tables if you're keeping that all in the same table, though. It'd be better to keep referals in a seperate table.
Get the row with the maximum in refer_count with a Date condition for your referal_date such that it's after the certainDate:
SELECT user_id FROM table WHERE refer_count = (SELECT MAX(refer_count) FROM table) AND referal_date>certainDate;
Note that WHERE is before SELECT so it will not get the highest count first, but will filter with the date condition then get the highest count.
Edit: Updated query based on edited question.
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.
I'm interesting in creating from scratch such feature to learn, I'm having a hard time understanding and being able to plan such structure. Most programmers from what I've seen
check if someone has already rated the comment or post by their IP address, I dislike that specially due to the fact that each user must be logged in to vote, so why not check the
user iD instead of iP. I also tend to choose performance instead of looks so could someone help me understand how to begin to accomplish this. I've read and searched many posts
on SO(StackOverflow) yet they're not too comprehensive for me. I also read somewhere to have a UNIQUE KEY constraint to only allow one vote, is that correct?
Some suggestions on a table structure and maybe how to design would be greatly appreciated.
I'm confused with such structure, the following tables is what I've got so far.
Posts:
post_iD | message | uid_fk(user id) | voteUp | voteDown
PostsRateSystem:
rate_iD | rate_user_iD(uid_fk from posts table) |
Do I even need a second table to keep track of who has rated.
if(isset($_POST['plus']) && isset($_POST['minus'])){
$plus = $_POST['plus'];
$minus = $_POST['minus'];
$row = insertRate($post_iD, $plus, $minus);
}
<form method="post" action="">
Thumbs Up
Thumbs Down
function insertRate($post_iD, $plus, $minus)
{
if($plus)
{
$sth = $this->db->prepare("UPDATE posts SET voteUp = 1 WHERE post_iD = :postiD");
$sth->execute(array('postiD' => $post_iD));
}
if($minus)
{
$sth = $this->db->prepare("UPDATE posts SET voteDown = 1 WHERE post_iD = :postiD");
$sth->execute(array('postiD' => $post_iD));
}
}
If all users can rate each post once, you'll have to have a third table (votes) that has a userid, a postid and a rating value (either 1 or -1). In that table, you should make the combination of userid and postid unique, because a user is only allowed one vote per post.
To get the total number of votes for a post, you can just sum those values:
SELECT SUM(votes.value) FROM votes WHERE postid = 10
Now, each upvote is counted as one, and each downvote as minus one, because those are the actual values stored.
To save a vote, you'll have to try to update the table first, because the logged in user can already have a vote. If updating fails, probably due to no vote already exists, you can insert a vote for the user for that post.
To speed up things, you can also increment or decrement a vote counter in the post table itself. That one doesn't keep track of all users, but it does keep track of the total score. That way, you don't have to query the sum for each page view of a vote. Keep in mind though, that if a use changes their upvote to a downvote, you'll have to subtract 2 from that score and vice versa. Alternatively, you can use the SELECT SUM query to calculate the votes and store it in the post, after each change to the votes of a post. So after voting for post 10, do this:
UPDATE posts
SET score = (SELECT SUM(value) FROM votes WHERE postid = 10)
WHERE postid = 10
We have to make a ballot system that makes users vote for various candidates on different positions. There's a login for voters. How do you store a vote of one voter and then get that vote added to the previous votes? The only possible way is to store every vote on a database right? But what would the structure of the database look like? And how do you count it?
edit:
The voting system doesnt only have one group of candidates to vote. It has mayor, vice-mayor, senator, etc. There are too many. that's why I'm confused. If only it was just a voting system of a president, it would be easier. So if I have a table for the voter, with a column of his/her voted candidate, it's not possible since the voter votes for many candidates.
A better way would be to have a different table to store Votes. And that table will have two attributes (VoterId, CandidateId)
And you can fetch the Vote Count if you are allowing multiple votes from this table..
But it would be better to make VoterId a Primary key in this table.. To avoid multiple voting
CandidateType: - (TypeId(PK), typeName, maxVotePerVoterForThisType)
Voter Table: - (voterId(PK), voterName, otherInfo)
Candidate Table: - (candidateId(PK), candidateName, constituency,
otherInfo, TypeId(FK))
Votes:- (voterId(PK, FK), TypeId(PK, FK), candidateId(FK))
*EDIT:- Schema edited with changed requirement in original post
*EDIT: - Added a field in CandidateType table to allow multiple votes.(E.g.: Now a voter can vote for 10 Senators, if maxVotePerVoter for this type is set to 10..)
You should e store each candidate in a table, positions in another table then make relations based on ID, the voting system is relatively simple:
database:
id, position_id, candidate_id, votes
then PHP
$query = "UPDATE `votes` SET `votes`=`votes`+1 WHERE `position_id`=1 AND candidate_id=1"; // adds 1 vote where position_id is 1 and candidate_id is 1
These 3 tables are required for your accounts (the voters) and candidates.
Account: Id (PK), Name, Password
Candidates: Id (PK), Name
Votes: AccountId (PK), CandidateId (PK)
Insert a row into votes when a vote is cast. This prevents duplicate voting due to the PK's.
I simple language i can say you are storing voting users in a table called users and you can use a field vaotecount (int) type initilize with zero and track their ip and vote for topic id on which they have voted for and when they vote you can check with IP and topic_id that they are not voting twice and if they passed you can increment the vaotecount field by one
and while you are counting for votes count the users with topic id for which you counting votes. Simple :D
From the explanation of problem, I can suggest you the following database structure:
tlbUserVote:
UserID(PK) | UserName | Vote | CandidateId(FK)
tlbCandidate:
CandidateId(PK) | CandidateName | TotalVotes
By this structure you can add the current votes with previous ones (by taking previous votes first and adding it to the current).
Total votes of the candidates will get updated, too.
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