how to make mysql structure of up,down rating - sql

Every programmer here knows about ratings like that:
Rating system http://img69.imageshack.us/img69/4241/98948761.gif
The problem is that I don't know how to make my SQL structure for that.
I think I can add up and down field in article table in MySQL, but this does not allow multi voting.
Could you please tell me how to make it?
Do I have to create a new table?

The easiest way is to simply store the vote counter per article.
When an article gets voted up, increase the counter. Voting down - decrease the counter.
If you need to keep track about which user voted up/down (and avoid multiple votes), you need to define an intersection table between users and articles.
It could look like this:
article_votes
--------------
user_id
article_id
vote
where vote can be either +1 or -1.
If you need the points of an article, you get it by
SELECT SUM( vote )
FROM article_votes
WHERE article_id = <your_article_id>

You may get some ideas out of how stackoverflow does it:
Stack Overflow Creative Commons Data Dump
Understanding the StackOverflow Database Schema
Meta Stackoverflow: Anatomy of a data dump

Related

How to get random unread article?

Database table articles contains 10.000 rows (articles)
I want to get a random article, mark it as read and never get it again,
so my next get request should return random article except that one.
There are thousands of users like me and all of them are using this table.
How do I implement this? How do I mark those articles, and how do I search only unread articles?
I was trying to create the column relatedUsers in the Articles table, which is filled out with relations to user objects, who has read the article. I was using search query: relatedUsers.objectId!=currentUserId But when this column get 2+ relatedUsers, this solutions stops working, because the database returns articles, which has at least one relatedUser, who's objectId isn't equal to currentUserId (means all users).
I'm using Backendless.com right now, but if I guess, the solution should be applicable to any database, including backendless. If not, then what kind of database should I use for this?
What you can do now is:
Create a table named UserArticle with fields articleId and userId
When you add a user or an article, you should also add to this table relations to all articles or users respectively; this way your UserArticle table will contain the relations between articles and users who haven't yet read those articles
And in order to get a random not read article for user, you:
Retrieve items from UserArticle where userId = yourCurrentUserId
Randomly select one of them and retrieve an article by its ID
Remove the selected article from UserArticle by articleId and userId, meaning that the user has read the article
This approach is the most suitable in your case. It involves a little more than one request, but for now your requirements cannot be fit better.
Also we (the Backendless team) are working on a keyword like contains, using which you would be able to have only a relation to Users table and get not read articles in one request, so the suggested approach is pretty temporary.
Getting a random number depends on the database. But the basic idea is to have a table called UserArticles with one row per user and per article already read (and perhaps other information such as the time).
Then, you can do:
select a.*
from articles a
where not exists (select 1
from userarticles ua
where a.article_id = ua.article_id and ua.user_id = $user_id
)
order by rand()
limit 1;
The order by rand() limit 1 is definitely database-dependent. But, it gives you the idea of how to approach the problem.
Once you have selected the article, then you insert this information into UserArticles:
insert into UserArticles(user_id, article_id)
values ($user_id, $article_id);
where $article_id refers to the article retrieved in the previous step.
Note: this should be fine with respect to race conditions, unless a single user can have multiple simultaneous connections to the database asking for the same information. Handling that case requires more knowledge about the database being used.

What is the best way to structure my database tables?

I'm having trouble designing my database schema.
I am having doubts on whether to separate or group tables.
I have a blog and a News section in my application and database. Both receive Comments and Likes.
TABLE.BLOG | TABLE.COMMENTS.BLOG | TABLE.LIKE.BLOG
TABLE.NEWS | TABLE.COMMENTS.NEWS | TABLE.LIKE.NEWS
I share everything?
TABLE.BLOG | TABLE.NEWS
TABLE.COMMENTS | TABLE.LIKE
or should I keep comments grouped?
In case I should have some kind of reference type blog | news?
I am very confused about the best way to structure the database.
I really appreciate if someone can help me.
Although the exact layout depends mostly on the way that you are going to access and use your data, you are probably going to be better off with comments and likes sitting in the same table. Your second approach is close, although I would probably introduce a third table, called CONTENT, with an ID of anything that could be liked or commented.
Each row in the NEWS and the BLOG table would have a corresponding row in the CONTENT table. A CONTENT row could correspond to either a NEWS or a BLOG, but not both. CONTENT table has attributes common to blogs and news (date, title, author, and so on).
The LIKE and COMMENT tables would then be connected to the CONTENT table, so you would not need to duplicate these two tables for NEWS and for BLOG.
Here is an illustration:
Best is subjective, though I'd have two tables:
Content (news and blog content, store like count)
Comment (news and blog comments)
and then add a type field on both that distinguishes between blog and news. That way you can reuse a lot of the db code.
IOW, minimize the number of tables if you can.

Database structure for voting system with up- and down votes

I am going to create a voting system for a web application and wonder what the best way would be to store the votes in the (SQL) database.
The voting system is similiar to the one of StackOverflow. I am pondering now if I should store the up and down votes in different tables. That way it is easier to count all up votes resp. down votes. On the other hand I have to query two tables to find all votes for an user or voted item.
An alternative would be one table with a boolean field that specifies if this vote is an up or down vote. But I guess counting up or down votes is quite slow (when you have a lot of votes), and an index on a boolean field (as far as I know) does not make a lot of sense.
How would you create the database structure? One or two tables?
Regarding the comments, we found the solution that best fits to Zardoz
He does not want to always count votes and needs as much details as possible. So the solution is a mix of both.
Adding an integer field in the considered table to store vote counts (make sure there won't be overflows).
Create additional tables to log the votes (user, post, date, up/down, etc.)
I would recommend to use triggers to automatically update the 'vote count field' when inserting/deleting/updating a vote in the log table.
If your votes are just up/down then you could make a votes table linking to the posts and having a value of 1 or -1 (up / down). This way you can sum in a single go.
https://meta.stackexchange.com/questions/1863/so-database-schema
Worth a look or
http://sqlserverpedia.com/wiki/Understanding_the_StackOverflow_Database_Schema
You will need a link table between users and the entities which are being voted on, I would have thought. This will allow you to see which users have already voted and prevent them from submitting further votes. The table can record in a boolean whether it is an up or down vote.
I would advise storing in the voted entity a current vote tally field to ease querying. The saving in size would be negligible if you omitted this.

Getting 5level relation with one mysql query

Thank u a lot for your answers beforehand. I need to make a such thing
I have a table friendship (id,user_id,friend_id,status,timestamp)
So lets say I am a user with user_id=43 and I am visiting a user with user_id=15
In the profile it should be a connection line of friendships
Let me describe ... lets say I have a friendship with user (user_id=3 and the user with user_id=3 is friend with user which profile I am visiting.
So on web site I will see
Connection
MyIcon->UserIcon(15)->UserIcon(3)->UserIcon(i am visiting)
And only in case when the friendship statuses for all are status=1...
Can anybody tell me how the query should look like?
With plain MySQL, there is no native way to do this. You have to either decide how deep you want to look, and use that amount of JOIN operations to see if you can 'reach' from one user id to the other, or you could give the community contributed Graph engine a whirl:
http://openquery.com/products/graph-engine
(this involves using a non-official binary AFAIK, perhaps it is already availble as a plug-in, but I am not sure aobut that)
With that engine, you can do it in a single simple query:
SELECT * FROM foo WHERE latch = 1 AND origid = 15 AND destid = 43;
And this would then return one row for each link you have to travel to reach from user 15 to user 43. You'd use the application code to display it nicely.
Had you modeled this as a Nested Set modeled hierarchy instead of the Adjacency List model which you have then this query would be trivial. As it is, you're looking at having to use recursion, which isn't natural to a relational database.
For some great information on modeling hierarchies, check out Joe Celko's book.
You might look at this answer to a question about recursive selection to see a hack you can do on the mySql side of things. It shows how to create a hierarchy for selection.
In mySql (ANSI SQL), there is no "native" way to perform such a query.

What is the best way to store a threaded message list/tree in SQL?

I'm looking for the best way to store a set of "posts" as well as comments on those posts in SQL. Imagine a design similar to a "Wall" on Facebook where users can write posts on their wall and other users can comment on those posts. I need to be able to display all wall posts as well as the comments.
When I first started out, I came up with a table such as:
CREATE Table wallposts
(
id uuid NOT NULL,
posted timestamp NOT NULL,
userid uuid NOT NULL,
posterid uuid NOT NULL,
parentid uuid NOT NULL,
comment text NOT NULL
)
id is unique, parentid will be null on original posts and point to an id if the row is a comment on an existing post. Easy enough and super fast to insert new data. However, doing a select which would return me:
POST 1
COMMENT 1
COMMENT 2
POST 2
COMMENT 1
COMMENT 2
Regardless of which order the rows existed in the database proved to be extremely difficult. I obviously can't just order by date, as someone might comment on post 1 after post 2 has been posted. If I do a LEFT JOIN to get the parent post on all rows, and then sort by that date first, all the original posts group together as they'd have a value of null.
Then I got this idea:
CREATE TABLE wallposts
(
id uuid NOT NULL,
threadposted timestamp,
posted timestamp,
...
comment text
)
On an original post, threadposted and posted would be the same. On a comment, timestamp would be the time the original post was posted and "posted" would be the time the comment on that thread was posted. Now I can just do:
select * from wallposts order by threadposted, posted;
This works great, however one thing irks me. If two people create a post at the same time, comments on the two posts would get munged together as they'd have the same timestamp. I could use "ticks" instead of a datetime, but still the accuracy is only 1/1000 of a second. I could also setup a unique constraint on threadposted and posted which makes inserts a bit more expensive, but if I had multiple database servers in a farm, the chance of a collision is still there. I almost went ahead with this anyway since the chances of this happening are extremely small, but I wanted to see if I could eat my cake and still have it too. Mostly for my own educational curiosity.
Third solution would be to store this data in the form of a graph. Each node would have a v-left and v-right pointer. I could order by "left" which would traverse the tree in the order I need. However, every time someone inserts a comment I'd have to re balance the whole tree. This would create a ton of row locking, and all sorts of problems if the site was very busy. Plus, it's kinda extreme and also causes replication problems. So I tossed this idea quickly.
I also thought about just storing the original posts and then serializing the comments in a binary form, since who cares about individual comments. This would be very fast, however if a user wants to delete their comment or append a new comment to the end, I have to deserialize this data, modify the structure, then serialize it back and update the row. If a bunch of people are commenting on the same post at the same time, I might have random issues with that.
So here's what I eventually did. I query for all the posts ordered by date entered. In the middle ware layer, I loop through the recordset and create a "stack" of original posts, each node on the stack points to a linked list of comments. When I come across an original post, I push a new node on the stack and when I come across a comment I add a node to the linked list. I organize this in memory so I can traverse the recordset once and have O(n). After I create the in-memory representation of the wall, I traverse through this data structure again and write out HTML. This works great and has super fast inserts and super fast selects, and no weird row locking issues; however it's a bit heavier on my presentation layer and requires me to build an in memory representation of the user's wall to move stuff around so it's in the right order. Still, I believe this is the best approach I've found so far.
I thought I'd check with other SQL experts to see if there's a better way to do this using some weird JOINS or UNIONS or something which would still be performant with millions of users.
I think you're better off using a simpler model with a "ParentID" on Comment to allow for nesting comments. I don't think it's usually a good practice to use datetimes as keys, especially in this case, where you don't really need to, and an identity ID will be sufficient. Here's a basic example that might work:
Post
----
ID (PK)
Timestamp
UserID (FK)
Text
Comment
-------
ID (PK)
Timestamp
PostID (FK)
ParentCommentID (FK nullable) -- allows for nested comments
Text
Do you want people to be able to comment on other comments, i.e. does the tree have infinite depth?
If you just want to have posts and then comments on those posts then you were on the right lines to start with and I believe the following SQL would meet that requirement (Untested so may be typos)
SELECT posts.id,
posts.posted AS posted_at,
posts.userid AS posted_by,
posts.posterid,
posts.comment AS post_text,
comments.posted AS commented_at,
comments.userid AS commented_by,
comments.comment AS comment_text
FROM wallposts AS posts
LEFT OUTER JOIN wallposts AS comments ON comments.parent_id = posts.id
ORDER BY posts.posted, comments.posted
This technique, a self-join, simply joins the table to itself using table aliases to specify the joins.
You should look into "nested sets". They allow retrieving a hierarchy very easily with a single query.
Here's an article about them
If you are using SQL server 2008, it has built-in support for it through the "hierarchyID" type.
Inserts and updates are more costly and complicated if you don't have the built in support), but querying is much faster and easier.
EDIT:
Damn, missed the part where you already knew about it. (was checking from a mobile phone).
If we stick to your table design … I think you would need some special value in the parentid column to separate original posts from comments (maybe just NULL, if you change definition of that column to nullable). Then, self-join will work. Something like this:
SELECT posts.comment as [Original Post],
comments.comment as Comment
FROM wallposts AS posts
LEFT OUTER JOIN wallposts AS comments
ON posts.id=comments.parentID
WHERE posts.parentID IS NULL
ORDER BY posts.posted, comments.posted
The result set shows Original Post before every comment, and has the right order.
(This was done using SQL Server, so I'm not sure if it works in your environment.)