SQL Help for Photo Battle Blog
Posted February 21st, 2008 @ 11:03am by Erik J. Barzeski
The Photo Battle Blog keeps a simple table that contains the IP address, the vote choice ("1" or "2"), and the post_id.
I'd like to add a feature to the plugin that drives the entire process that allows me to get some statistics. I've already written up the code to generate the easy statistic: total votes for "1" vs. total votes for "2."
But I'd also like to show the "win" totals, where a single post_id is the "battle." If I win one day 40-7 but Steve wins the next day 33-32, our "win" totals would be 1-1.
So the question, then, is this: what's the best combination of MySQL and PHP to generate that information? Pulling down the entire table and parsing it via PHP may be easy on the MySQL side, of course, but leaves a lot to be desired on the PHP side. I think, though I can't be sure, that much of the calculating can be done via MySQL. Any DB wizards out there know?
P.S. Once this problem is solved, Aaron and I may clean up the plugin and offer it as donationware to others who want to run a Photo Battle Blog.
Posted 22 Feb 2008 at 11:36am #
I think something like the following will do what you want:
select
post_id,
ones.votes as one_vote,
twos.votes as two_vote,
case when ones.votes > twos.votes then 1 else 2 end as winner
from
(select post_id, count(*) as votes from votes where choice = 1 group by post_id) as ones join
(select post_id, count(*) as votes from votes where choice = 2 group by post_id) as twos on
ones.post_id = twos.post_id
Although, that syntax might not work with MySQL. It works for Postgresql and SQL Server.
Posted 22 Feb 2008 at 11:39am #
Oh, forgot the edge case of a tie. That's just another addition to the case statement returning a value to represent a tie, maybe 0.
Posted 22 Feb 2008 at 12:12pm #
I'm guessing you either have:
1. limit the ability to vote one time per IP/Post, or 2. have a unique index on the votes table (ip_address, post_id)?
Either way, Patrick's solution appears to do the trick on MySQL.
1 representing true.
Posted 25 Feb 2008 at 4:04pm #
I have never used MySQL, only T-SQL (microsoft sql server), so my queries might not work on your system out of the box. Stupid nonstandard language 👿
Might I suggest setting your table up so that the choice/vote column is only ever 1 or 0. ("1" = vote for photo 1, "0" = vote for photo 2). This should allow you to build slightly faster queries by not having to search on the vote column (also lets you use BIT, but then you might have to convert() later, I'm not sure how MySQL handles bit->int conversions. At least in the tests I did locally, MS SQL won't do the conversion implictly, but I didn't see any performance drops adding an explicit conversion).
The first stat you already have:
The second stat ("wins") you're looking for:
or in aggregate form:
Alternatively/at the very least, you should modify Patrick's logic to include cases where 1 photo receives all of the votes:
Posted 25 Feb 2008 at 6:51pm #
That's quite a bit to select constantly. Joins are expensive. I'd create a third table that'd hold the post_id and who won. Then create a trigger - on close of a battle it updates. (or if you don't want to mess with a trigger a cron'd script or something that kicks off a script that'll handle it.