I want to include a like/dislike system similar to Facebook and so far, I have set the like/dislike columns as a 'text' type. This is so that I can add the id for the user(s) who liked/disliked a post. Would that be the best way of doing it? Also, in addition to the question above, how would I stop a user pressing the like and dislike button again? Since, once a user has liked a post, it should display an unlike/undislike option? A concept/idea would be great of how to do this.
While it's hard to make armchair decisions, here are my ideas:
First, you could have a 'likes' integer column for each post. When a user clicks up and down, have that number increment or decrement. This offers no protection against users clicking multiple times, but it's easy and fast.
Another way would be to have a 'Like' table, with columns post_id, user_id, and score. score can have two values: '1' or '-1'. All 3 columns are integers. When the user clicks 'like', you do an INSERT/UPDATE command on the row with user_id & post_id matching.
Then, to see the final score for a post, you do a SELECT SUM(score) FROM that_table WHERE post_id = ?.
With this second method, if you wanted to see the name of the most recent clicker, you could add a timestamp column and search for the most recent entry.
I would create a table with the following structure:
Table: Likes
PostId bigint
UserId bigint
Like bit(true, false)
Then set PostId and UserId together as the primary key. This will prevent the database from inserting multiple likes/unlikes for the same post.
In your code, check to see if the user/post combination exists and then toggle the bit value if it does or set the bit value to true if it does not.
Related
Say I'll get all the followers of a certain content from my project; here is my db
table
contents
users
Now, everytime I want to get content's numbers of followers, I have this table here to get connections with users called content-followers.
table
contents
users
content-followers <
columns
user_id
content_id
Now my concern is say this will run getting the numbers of followers of a content, but this will be along with the other queries and stuff and I understand it may get the sql slower on process.
See, everytime people will visit the content, I'll have to show that count, but that count (as I imagine) will run through the entire table just to count.
Is there other way to make it simple? Like counting only once a certain time and save to contents table?
I have no proper database lessons so, thanks guys for your help in advance!
CREATE TABLE ContentFollowers (
user_id ...,
content_id ...,
PRIMARY KEY(user_id, content_id),
INDEX(content_id, user_id)
) ENGINE=InnoDB;
SELECT ...,
( SELECT COUNT(*) FROM ContentFollowers
WHERE user_id = u.id
) AS follower_count
FROM Contents AS c
JOIN Users AS u ON ...
WHERE ...
The COUNT(*) will efficiently use the PRIMARY KEY of ContentFollowers. The added time taken will be a few milliseconds, even with many millions of users and contents.
If you want to discuss further, please provide the SHOW CREATE TABLE for each relevant table and your tentative SELECT (which will have more than what I specified). So "... counting only once ..." should be unnecessary (and a hassle).
Is it possible for a "user" to "follow" a "content" more than once? This is a potential hack to mess up your numbers, but I think what I say here avoids that possibility. (A PRIMARY KEY includes an 'uniqueness' constraint.) Without this, a user could repeatedly click on [Follow] to inflate the number of 'followers'.
In what you have specified so far, I don't see the need for a TRIGGER. Furthermore, a Trigger would reopen the possibility of the above 'hack'.
So I've found through researching myself that the best way I can design a structure for liking posts is by having a database like the following. Let's say like Reddit, a post can be upvoted, downvoted, or not voted on at all.
The database would then having three columns, [username,post,liked].
Liked could be some kind of boolean, 1 indicating liked, and 0 indicating disliked.
Then to find a post like amount, I would do SELECT COUNT(*) FROM likes WHERE post=12341 AND liked=1 for example, then do the same for liked=0(disliked), and do the addition server side along with controversy percentage.
So I have a few concerns, first off, what would be the appropriate way to find out if a user liked a post? Would I try to select the liked boolean value, and either retrieve or catch error. Or would I first check if the record exist, and then do another select to find out the value? What if I want to check if a user liked multiple posts at once?
Secondly, would this table not need a primary key? Because no row will have the same post and username, should I use a compound primary key?
For performance you will want to alter your database plans:
User Likes Post table
Fields:
Liked should be a boolean, you are right. You can transform this to -1/+1 in your code. You will cache the numeric totals elsewhere.
Username should be UserID. You want only numeric values in this table for speed.
Post should be PostID for the same reason.
You also want a numeric primary key because they're easier to search against, and to perform sub-selects with.
And create a unique index on (Username, Post), because this table is mainly an index built for speed.
So did a user vote on a post?
select id
from user_likes_post
where userID = 123 and postID = 456;
Did the user like the post?
select id
from user_likes_post
where userID = 123 and postID = 456 and liked = true;
You don't need to worry about errors, you'll either get results or you won't, so you might as well go straight to the value you're after:
select liked from user_liked_post where userID=123 and postID=456
Get all the posts they liked:
select postID
from user_likes_post
where userID = 123 and liked = true;
Post Score table
PostID
TotalLikes
TotalDislikes
Score
This second table will be dumped and refreshed every n minutes by calculating on the first table. This second table is your cached aggregate score that you'll actually load for all users visiting that post. Adjust the frequency of this repeat dump-and-repopulate schedule however you see fit. For a small hobby or student project, just do it every 30 seconds or 2 minutes; bigger sites, every 10 or 15 minutes. For an even bigger site like reddit, you'd want to make the schema more complex to allow busier parts of the site to have faster refresh.
// this is not exact code, just an outline
totalLikes =
select count(*)
from user_likes_post
where postID=123 and liked=true
totalDislikes =
select count(*)
from user_likes_post
where postID=123 and liked=false
totalVotes = totalLikes + totalDislikes
score = totalLikes / totalVotes;
(You can simulate an update by involving the user's localStorage -- client-side Javascript showing a bump-up or down on the posts that user has voted on.)
Given your suggested 3-column table and the selects you suggest, be sure to have
PRIMARY KEY(username, post) -- helps with "did user like a post"
INDEX(post_id, liked) -- for that COUNT
When checking whether a user liked a post, either do a LEFT JOIN so that you get one of three things: 1=liked, 0=unliked, or NULL=not voted. Or you could use EXISTS( SELECT .. )
Tables need PKs.
I agree with Rick James that likes table should be uniquely indexed by (username, post) pair.
Also I advise you to let a bit redundancy and keep the like_counter in the posts table. It will allow you to significantly reduce the load on regular queries.
Increase or decrease the counter right after successful adding the like/dislike record.
All in all,
to get posts with likes: plain select of posts
no need to add joins and aggregate sub-queries.
to like/dislike: (1) insert into likes, on success (2) update posts.like_counter.
unique index prevents duplication.
get know if user has already liked the post: select from likes by username+post pair.
index helps to do it fast
My initial thought was that the problem is because boolean type is not rich enough to express the possible reactions to a post. So instead of boolean, you needed an enum with possible states of Liked, Disliked, and the third and the default state of Un-reacted.
Now however it seems, you can do away with boolean too because you do not need to record the Un-reacted state. A lack of reaction means that you do not add the entry in the table.
What would be the appropriate way to find out if a user liked a post?
SELECT Liked
FROM Likes
WHERE Likes.PostId == 1234
AND Likes.UserName == "UniqueUserName";
If the post was not interacted with by the user, there would be no results. Otherwise, 1 if liked and 0 if disliked.
What if I want to check if a user liked multiple posts at once?
I think for that you need to store a timestamp too. You can then use that timestamp to see if it there are multiple liked post within a short duration.
You could employ k-means clustering to figure if there are any "cluster" of likes. The complete explanation is too big to add here.
Would this table not need a primary key?
Of course it would. But Like is a weak entity depending upon the Post. So it would require the PK of Post, which is the field post (I assume). Combined with username we would have the PK because (post, username) would be unique for user's reaction.
I'm trying to get make my table get only 1 of the results that I have in the table. My table stores a posted_by column that allows more than 1 user to post more than once. So, my posted_by column has multiple of the username Chowderrunnah because that user has posted more than once. I only want to display one of the results so each user can be clicked on in the table instead of displaying many of the same user. (I'm trying to create a support ticket system if you're wondering why).
At the moment, my table is getting all the results from my table, even duplicates with the same posted_by (username). I only want it to display one result if there are more than 1 of the same posted by (username).
At the moment, my code is only the minimum -
SELECT * from support WHERE last_post_by != 'Admin'
Help is much appreciated.
Edit
Here is the table I'm using
Here is my support ticket system
The following Query fixed my problem :)
SELECT * from support WHERE last_post_by != 'Admin' GROUP BY posted_by
Using GROUP BY allowed me to only display one of the duplicate posted_by results.
I am currently making an Android app where I want to allow users to rate posts, posted by other users.
This is my current design, however, would I include Rating as a separate table or included in the Media upload table?
If it is a separate table would it be something like this?
Rating table: (Rating, PostID, Rating_Count, Average_Rating)
If anything else looks wrong with my design that would also be appreciated!
It all depends on how you want the ratings to work, who gets to up-vote or down-vote, whether you track everyone that votes to keep from having multiple votes from the same person, etc.
This is what I would do: add a unique numeric (long?) RatingID field for each vote, link the PostID field to the Comments table, add a Rating (integer) field that is limited to values from 0 to 5 (or whatever range you prefer), and then calculate the average from all votes cast (delete the Rating_Count and Average_Rating fields). You could define a view to calculate the average ratings for each post to use for your routine for determining how to display it next to the Post.
Also, I would use an ID for each user, not use their names.
So my table would look like this:
RatingID, PostID, UserID, Rating
Also, to keep users from voting multiple times, the table would not allow multiple entries for the same PostID and UserID.
It depends on how you want to store the Rating.
If you want to track each individual rating. You will need a Rating table with the following:
Column
---------
RatingID (PK)
Username (FK) (UserDetailsID if you change the primary key of the UserDetails table)
PostID (FK)
RatingScore
DateRated
In the app, you would then pull back every RankScore with that PostID and do the calculation based off a count of said PostID.
I would recommend the above method because it will allow you do more things with the Ratings in the future. Plus, you have a good structure to follow in place with your Comments table. ex. Show who Rated what and at what score.
If you want to attach it to the Post table, another table is not needed. You would simply add Rating_Count and Average_Rating to the table. In your app, you would than have to perform an update every time the Post is rated. You would have to pull the Rating_Count and Average_Rating, increment the Rating_Count by one, recalculate the Average_Rating and perform the update.
My second suggestion is less flexible if you ever want to enhance your Rating setup though.
you can use stretcher like this
enter code here
$table->bigIncrements('id');
$table->bigInteger('user_id')->unsigned();
$table->ipAddress('ip')->nullable();
$table->integer('star');
$table->string('type');
$table->string('title');
$table->string('average')->nullable()->default(0);
$table->integer('count_star')->default(0)->nullable();
$table->float('star_avg')->default(0)->nullable();
$table->float('unstar_avg')->default(0)->nullable();
I would like to know which is the best when I want to check if somebody already participated to a members related event (like a poll):
Imagine that I have a table that stores all the voters votes. Over time, it can reach a very big size (10000+ entries/votes for 500 different polls).
When I want to check if a member has already voted to my new poll, what's the best? :
1/ Make a SELECT or a COUNT on the "10000+ entries VOTERS table" to see if said USERID already voted to my new poll.
2/ Having a TEXT columns in my POLL_main_infos Table where i stock/CONCAT the USERIDS like these:
"1,15,42,12,523,8521,7444, etc etc."
And to check, I get that columns as a variable then in my PHP script, I use a REGEX to check if a USERID is already present in it (like looking for ",42,", meaning the USER with the ID "42" already participated to said poll.
Also, if the second solution is the best, should I stock the IDS in a text column or a BLOB?
Anyways, thank you very much in advance!
In a Relational Database system it's usually best to store data normalized, so no list of user ids, never.
So using Holmes IV suggested tables it's a simple:
SELECT 'User already voted'
WHERE EXISTS
(
SELECT * FROM Participation
WHERE PollId = 123
AND UserId = 456
)
With an appropriate index (a UNIQUE index if a user might vote only once per poll) this will be very fast regardless of the number of rows.
How much freedom do you have with this? I would say you need 3 tables, 1 for each : Polls, Voters, Participation . In the Polls table you store Poll_ID and Poll_Description. In voter you store all Voter_ID and any other voter information you might have. Then in the participation you store, Poll_ID,Voter_ID, and results or simply a date of when they participated. Then when you want to see such information you query the 3rd table, and join it on the other too for the specifics
The best way to check if a user already voted is to query the Polls_Voters tables in your setup it would be
Select *
from Polls_Voters
where Voter_ID = 'ID of voter you are looking for'
and poll_id = 'ID of poll you wish to see if they voted in'