Let's say I have a table like this (this is just a simplified example, the real table I'm talking about is much more complex):
CREATE TABLE media (
id INT NOT NULL AUTO_INCREMENT,
title VARCHAR(255) NOT NULL,
description TEXT NOT NULL,
voted INT NOT NULL DEFAULT 0,
rating FLOAT NOT NULL DEFAULT 0
) ENGINE = INNODB;
The voted column represents a number of votes the item has received and the rating column represents the total rating of the item.
Now, what I want to do is select a single item from the table based on id, something like:
SELECT m.* FROM media AS m WHERE id = 5;
But, in addition, I want to calculate the position of this row based on the rating column and fetch that as an additional column, let's say called a site_rank (so the bigger the rating of the row is the higher its site_rank will be, I hope I explained it well). My guess is this can be achieved with a subselect query but I'm not sure how to do it.
Any help?
SELECT count(*) FROM media WHERE rating > (SELECT rating FROM media WHERE id = 5);
This will output high rank for most voted media.
If you want low rank for the most voted (like, the most voted gets the rank of 1), just reverse the sign in the subquery.
SELECT mo.*,
(
SELECT COUNT(*)
FROM media mi
WHERE (mi.rating, mi.id) <= (mo.rating, mo.id)
) AS rank
FROM media mo
WHERE mo.id = 5
SELECT
m1.*,
(SELECT COUNT(*) FROM media AS m2 WHERE m2.rating > m1.rating) AS site_rank
FROM
media AS m1
WHERE
id = 5;
Note that this does not define a complete ordering because there might be items with equal rating and they will report the same site rank.
Does MySQL support the standard SQL rank() function? It does exactly what you want.
select
*,
rank() over (
order by rating desc
) as site_rank
from media
Related
Well I have this mysql table with numbers in one column and a confirmation boolean of 0 or 1 and I have about 1,000 rows so it's not something I can do manually but anyways...
I want to sort the row by highest value and grab the names of the first 5 people and put those 5 people in another table on a column and then set them to confirmed and continue until there's no one left in the table that isn't confirmed...
ex:
Name:Rank:Confirm
Bob:5000:0
James:34:0
Josh:59:1
Alex:48:0
Romney:500:0
Rolf:24:0
Hat:51:0
so when you run the code it will do the following:
Squad:Name1:Name2:Name3:Name4:Name5
1:Bob:Romney:Hat:Alex:James
(as you can see Josh was excluded and Rolf was too low)
And since Rolf is alone and there are no one else left, he wont be put into a team and will be left unconfirmed...
I'm not really pro at mysql so I was stumped on this and at most was capable of organizing the whole thing by rank and that's it ._.
edit:
The terrible attempt I had at this:
<?php
$parse = mysql_query("SELECT MAX(rank) AS rank FROM users AND confirm='0'");
mysql_query("Insert into squad (nameone)values($parse)");
mysql_query("Update squad set confirm = '1' where name = $parse");
?>
Assuming confirm will have only either 1 or 0.
CREATE TABLE table2 (id INT PRIMARY KEY AUTO_INCREMENT, name varchar(255));
CREATE PROCEDURE rank()
BEGIN
DECLARE count INT DEFAULT 1;
WHILE count > 0 DO
UPDATE table1 SET Confirm=2 WHERE Confirm=0 ORDER BY Rank DESC LIMIT 5;
INSERT INTO table2 (SELECT GROUP_CONCAT(Name) FROM table1 WHERE Confirm=2);
UPDATE table1 SET Confirm=1 WHERE Confirm=2;
SELECT count(*) FROM table1 WHERE Confirm=0;
END WHILE;
END;
Call the procedure rank() when ever you want
CALL rank();
I am writing a web app in PHP using mySQL that models an election.
I have three tables: Candidates, Elections, and Votes. Votes contains CandidateID, ElectionID and Count, which is the number of times that the given candidate was voted for in the given Election. Votes also contains TimeStamp which is the last time the row was modified which is used for breaking ties (the earlier vote wins). A candidate may have run in multiple elections. How do I find how many elections a given candidate has ever won?
All help greatly appreciated, thanks.
Some sample data:
CREATE TABLE IF NOT EXISTS `Votes` (
`ElectionID` int(11) unsigned NOT NULL,
`CandidateID` int(11) unsigned NOT NULL,
`Count` smallint(5) unsigned NOT NULL DEFAULT '0',
`stamp` int(11) unsigned NOT NULL,
PRIMARY KEY (`ElectionID`,`CandidateID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
INSERT INTO `Votes` (`ElectionID`, `CandidateID`, `Count`, `stamp`)
VALUES
(1, 1, 3, 1332897534),
(4, 1, 3, 1333149930),
(4, 4, 2, 1333149947),
(4, 5, 3, 1333149947),
(1, 4, 4, 1333153373);
Desired output: One row, with one column, being the number of wins for a certain candidate
You can write:
SELECT COUNT(1)
FROM Elections AS e
INNER
JOIN Votes AS v1 -- representing the candidate of interest
ON v1.ElectionID = e.ID
AND v1.CandidateID = ...
LEFT
OUTER
JOIN Votes AS v2 -- representing a candidate who beat the candidate of interest
ON v2.ElectionID = e.ID
AND ( v2.Count > v1.Count
OR ( v2.Count = v1.Count
AND v2.stamp < v1.stamp
)
)
WHERE v2.ElectionID IS NULL -- meaning that no candidate beat the candidate of interest
;
(It's also possible to represent either or both of those joins with EXISTS and a correlated subquery; or the first join could be changed to IN with an uncorrelated subquery; but the above is the most likely to perform best, IMHO, and my experience on StackOverflow has been that people seem to like joins better than subqueries for some reason. If you'd prefer a subquery answer, let me know.)
SELECT CandidateID, MAX(Count) FROM Votes GROUP BY ElectionID
should do the trick
Your query basically needs to return every election and WHO Won it. Then apply that result to the specific candidate your are interested in finding out how many that person won out of all elections. Ex: in the U.S. Republican Race, you have 4 candidates... 2 are really the only real considered by most regardless of party affiliation. Each party runs their campaign in each state and they all have their respective votes tallied. So, at the end of ex: 20 states, you will only have 21 winners, but who won how many. Candidate "A" may win 10, "B" wins 6, "C" wins 3 and "D" wins 2. So if you wanted to know how many Candidate "B" won, your answer desired is 6... from my impression of your question.
This will give you all qualifying "First Place" elections for a given candidate. If all you care about is the HOW MANY, you can just change the Prequery.fields to COUNT(*). If you want to get the candidate's name and the name/info of the election, you can add that as join conditions AFTER the PreQuery has been executed.
select
PreQuery.idVotes,
PreQuery.CandidateID,
PreQuery.ElectionID,
PreQuery.Votes,
PreQuery.LastEntry
from
( select
v.*,
#WinRow := if( #LastElection = v.ElectionID, #WinRow +1, 1 ) as FinalPlace,
#LastElection := v.ElectionID as ignoreMe
from
Votes v,
( select #WinRow := 0, #LastElection := 0 ) sqlvars
order by
v.ElectionID,
v.Votes DESC,
v.LastEntry ASC ) PreQuery
where
PreQuery.FinalPlace = 1
AND PreQuery.CandidateID = CandidateIDYouAreInterestedIn
Basically what you want to do is group the Votes rows by ElectionID, and order by Count descending, stamp ascending. That will give you a result set of ordered Votes rows, with the "winners" of each election as the first row within each group.
Next, you want to select these first rows within each group and discard the rest (see here for how to do a Top-N query: http://www.sqlines.com/mysql/how-to/get_top_n_each_group).
Finally, you want to select count(*) from this result set where CandidateID = whatever candidate you're looking for. Alternatively, you can group by CandidateID and leave out the where clause if you want the number of wins for all candidates instead of a specific one.
Hope this helps.
Apparently I'm late to the party, but this is the first thing I though of:
First have one subquery to select the winning count for each unique electionid in Votes, call this table wins.
Then, join wins with Votes where electionid and count are equal. Because there may be a tie, we also need to choose the Votes row with the lowest stamp, so we'll group by electionid and count but this time choose the minimum stamp. We'll call this resulting table wins_with_stamp/wws for short.
Now, wins_with_stamp has all of the rows from Votes that are "winning" rows, so selecting how many a particular candidate won is just a matter of a where candidateid = ? clause.
-- Returns how many Votes rows that is the winner of its election
-- and candidateid is the candidate in question
select count(*)
from Votes v2
right join (
-- Gets the earliest stamp for the votes with the winning count for each election
select v.electionid, v.count, min(v.stamp) as minstamp
from Votes v
right join (
-- Gets the winning count for each election
select electionid, max(count) as max
from Votes
group by electionid
) wins on wins.max = v.count and wins.electionid = v.electionid
group by electionid, count
) wws on wws.count = v2.count and wws.electionid = v2.electionid and wws.minstamp = v2.stamp
where candidateid = [YOUR_CANDIDATEID]
I am what you would call a 'noob' at MySQL. I can insert/edit/select stuff, but anything more advanced than that stumps me. I have two tables in my database:
Table 'reviews'
id int(11)
review varchar(2500)
game int(11)
user int(11)
title varchar(200)`
and Table 'review_rating'
user int(11)
review int(11) // Corresponds to `reviews.id`
like tinyint(1)
Here is my question: Is it possible to use ORDER BY on the reviews table to order the result by the total number of review_ratings with 'like' = 1 (where 'review' = the id of the 'reviews' table) divided by the total number of review_ratings (where 'review' = the id of the 'reviews' table).
Example:
SELECT *
FROM `reviews`
WHERE `game` = ?
ORDER BY (total number of review_ratings where review = reviews.id and like = 1 /
total number of review_ratings where review = reviews.id)
LIMIT 0, 10
SELECT t.review,
Score = CASE WHEN TotalReviews<> 0 THEN LikedReviews/TotalReviews ELSE NULL END
FROM (
SELECT *,
(SELECT COUNT(*) FROM review_rating WHERE review = r.review) AS TotalReviews ,
(SELECT COUNT(*) FROM review_rating WHERE review = r.review AND like = 1) AS LikedReviews,
FROM review r
WHERE game = ?
)t
ORDER BY t.review, Score
I think it's clearer to put it in the SELECT clause:
SELECT reviews.*,
( SELECT SUM(like) / COUNT(1)
FROM review_ratings
WHERE review = reviews.id
) like_ratio
FROM reviews
WHERE game = ?
ORDER
BY like_ratio DESC
LIMIT 10
;
Notes:
Not tested; I'm away from a MySQL box at the moment.
I think you could move the subquery to the ORDER BY clause if you wanted, but it seems like a useful thing to retrieve, anyway.
I'm not sure how the above will behave if a given review has no ratings. You may need to use a CASE expression to handle that situation.
something like this would order by the total review_rating per review:
select( count(review.id) as 'total' from reviews join review_rating on review.id = review_rating.review group by review.id) order by total
the math is not exactly what you had but hopefully you will get it
so I'm trying to create a ranking system for my website, however as a lot of the records have same number of points, they all have same rank, is there a way to avoid this?
currently have
$conn = $db->query("SELECT COUNT( * ) +1 AS 'position' FROM tv WHERE points > ( SELECT points FROM tv WHERE id ={$data['id']} )");
$d = $db->fetch_array($conn);
echo $d['position'];
And DB structure
`id` int(11) NOT NULL,
`name` varchar(150) NOT NULL,
`points` int(11) NOT NULL,
Edited below,
What I'm doing right now is getting records by lets say
SELECT * FROM tv WHERE type = 1
Now I run a while loop, and I need to make myself a function that will get the rank, but it would make sure that the ranks aren't duplicate
How would I go about making a ranking system that doesn't have same ranking for two records? lets say if the points count is the same, it would order them by ID and get their position? or something like that? Thank you!
If you are using MS SQL Server 2008R2, you can use the RANK function.
http://msdn.microsoft.com/en-us/library/ms176102.aspx
If you are using MySQL, you can look at one of the below options:
http://thinkdiff.net/mysql/how-to-get-rank-using-mysql-query/
http://www.fromdual.ch/ranking-mysql-results
select #rnk:=#rnk+1 as rnk,id,name,points
from table,(select #rnk:=0) as r order by points desc,id
You want to use ORDER BY. Applying on multiple columns is as simple as comma delimiting them: ORDER BY points, id DESC will sort by points and if the points are the same, it will sort by id.
Here's your SELECT query:
SELECT * FROM tv WHERE points > ( SELECT points FROM tv WHERE id ={$data['id']} ) ORDER BY points, id DESC
Documentation to support this: http://dev.mysql.com/doc/refman/5.0/en/sorting-rows.html
Many Database vendors have added special functions to their products to do this, but you can also do it with straight SQL:
Select *, 1 +
(Select Count(*) From myTable
Where ColName < t.ColName) Rank
From MyTable t
or to avoid giving records with the same value of colName the same rank, (This requires a key)
Select *, 1 +
(Select Count(Distinct KeyCol)
From myTable
Where ColName < t.ColName or
(ColName = t.ColName And KeyCol < t.KeyCol)) Rank
From MyTable t
Hello i have a question on picking random entries from a database. I have 4 tables, products, bids and autobids, and users.
Products
-------
id 20,21,22,23,24(prime_key)
price...........
etc...........
users
-------
id(prim_key)
name user1,user2,user3
etc
bids
-------
product_id
user_id
created
autobids
--------
user_id
product_id
Now a multiple users can have an autobid on an product. So for the next bidder I want to select a random user from the autobid table
example of the query in language:
for each product in the autobid table I want a random user, which is not the last bidder.
On product 20 has user1,user2,user3 an autobidding.
On product 21 has user1,user2,user3 an autobidding
Then I want a resultset that looks for example like this
20 – user2
21 – user3
Just a random user. I tried miximg the GOUP BY (product_id) and making it RAND(), but I just can't get the right values from it. Now I am getting a random user, but all the values that go with it don't match.
Can someone please help me construct this query, I am using php and mysql
The first part of the solution is concerned with identifying the latest bid for each product: these eventually wind up in temporary table "latest_bid".
Then, we assign randon rank values to each autobid for each product - excluding the latest bid for each product. We then choose the highest rank value for each product, and then output the user_id and product_id of the autobids with those highest rank values.
create temporary table lastbids (product_id int not null,
created datetime not null,
primary key( product_id, created ) );
insert into lastbids
select product_id, max(created)
from bids
group by product_id;
create temporary table latest_bid ( user_id int not null,
product_id int not null,
primary key( user_id, product_id) );
insert into latest_bid
select product_id, user_id
from bids b
join lastbids lb on lb.product_id = b.product_id and lb.created = b.created;
create temporary table rank ( user_id int not null,
product_id int not null,
rank float not null,
primary key( product_id, rank ));
# "ignore" duplicates - it should not matter
# left join on latest_bid to exclude latest_bid for each product
insert ignore into rank
select user_id, product_id, rand()
from autobids a
left join latest_bid lb on a.user_id = lb.user_id and a.product_id = lb.product_id
where lb.user_id is null;
create temporary table choice
as select product_id,max(rank) choice
from rank group by product_id;
select user_id, res.product_id from rank res
join choice on res.product_id = choice.product_id and res.rank = choice.choice;
You can use the LIMIT statement in conjunction with server-side PREPARE.
Here is an example that selects a random row from the table mysql.help_category:
select #choice:= (rand() * count(*)) from mysql.help_category;
prepare rand_msg from 'select * from mysql.help_category limit ?,1';
execute rand_msg using #choice;
deallocate prepare rand_msg;
This will need refining to prevent #choice becoming zero, but the general idea works.
Alternatively, your application can construct the count itself by running the first select, and constructing the second select with a hard-coded limit value:
select count(*) from mysql.help_category;
# application then calculates limit value and constructs the select statement:
select * from mysql.help_category limit 5,1;