views:

76

answers:

2

I have a voting script which stores the post_id and the user_id in a table, to determine whether a particular user has already voted on a post and disallow them in the future.

To do that, I am doing the following 3 queries.

SELECT user_id, post_id from votes_table where postid=? AND user_id=?

If that returns no rows, then:

UPDATE post_table set votecount = votecount-1 where post_id = ?

Then

SELECT votecount from post where post_id=?

To display the new votecount on the web page

Any better way to do this? 3 queries are seriously slowing down the user's voting experience

Edit

  • In the votes table, vote_id is a primary key
  • In the post table, post_id is a primary key.
  • Any other suggestions to speed things up?
+6  A: 

You can merge the first two queries:

UPDATE  post
SET     votecount = votecount - 1
WHERE   post_id = ?
        AND post_id NOT IN
        (
        SELECT  post_id
        FROM    votes_table
        WHERE   user_id = ?
        )

You still need to run the third one.

Make sure that you have a unique index or a PRIMARY KEY on votes_table (user_id, post_id) and post (post_id).

Most probably, your current query slowness is caused by absence of these indexes.

Quassnoi
i have a vote_id in votes_table. make that into the primary key there?
Sev
in the votes_table, vote_id is already a primary key. in the post table, post_id is also a primary key. am i missing something?
Sev
@Sev: `CREATE UNIQUE INDEX votes_user_post ON votes_table(user_id, post_id)`
Bill Karwin
@Bill: what is votes_user_post ?
Sev
@Bill: is that a new unique index you're creating, in addition to the primary key? If so, can you tell me or link me as to why it's necessary to have both a primary key AND a unique index?
Sev
@Sev: if a user can only vote once for or against the post, you need to create a unique index to police this constraint. This index will also speed up your query, since it will help to locate a user/post pair in the table instantly. If you don't reference `votes_table` from other tables, you can make (`user_id`, `post_id`) a `PRIMARY KEY` (because it's a many-to-many link table).
Quassnoi
got it. thanks for the help!
Sev
+1  A: 

Quassnoi's answer is going to pretty inefficient by virtue of pulling every post_id the user has ever voted on into the NOT IN.

UPDATE 
    dbo.Post
SET 
    VoteCount = VoteCount + 1 -- Must be +1 right? Not minus 1?
FROM
    dbo.Post p
WHERE
    NOT EXISTS
        (
        SELECT
            v.PostID
        FROM
            dbo.Votes_Table v
        WHERE
            v.PostID = p.PostID
        AND v.User_ID = ?
        )
WHERE
    p.PostID = ?

IF(@@ROWCOUNT = 1)
    PRINT 'No previous vote, votes incremented'
ELSE
    PRINT 'User has already voted, votes not incremented'

Personally, I'd seriously reconsider maintaining a vote_count on the post table.

Mark Storey-Smith
Minus 1 because I was referring to a down-vote script. Same concept though.
Sev
Also, I should have specified that my DB is MySQL not MSSQL
Sev
@Mark: why do you think it would be inefficient?
Quassnoi
@Quassnoi because you're selecting the post_id of every vote the user has ever made in the sub-query. If that user was you and the site was stackoverflow, thats 2200 rows rather than 1.
Mark Storey-Smith
@Mark - You'd reconsider maintaining a vote_count on the post table? So would you suggest running a select sum() on the votes table instead for every post?
Sev
Absolutely. I'd sum the votes table with isolation at read uncommitted. You might consider making the vote_count static once the question is closed, but while open do the sum(). I'm also struggling to understand why you have separate vote up and vote down scripts. If you prefer to maintain the vote_count, why not pass a parameter indicating the direction of the vote?
Mark Storey-Smith
@Mark: that's not how `IN` works in `MySQL`. The optimizer will push the `post_id` into the subquery, fetch the first record from `votes_table` with given `(user_id, post_id)` (using the index) and return. The whole query would require but two index seeks: one to retrieve a post by `post_id`, one to retrieve a vote by `(user_id, post_id)`. http://explainextended.com/2009/09/18/not-in-vs-not-exists-vs-left-join-is-null-mysql/
Quassnoi