I have a MYSQL5 database and PHP 5. I need a query for a games websites index page that only selects the first 12 from each category of games. Here is what I have so far.
$db->query("SELECT * FROM `games` WHERE status = 'game_published' AND `featured` = '1' ORDER BY `category`");
The php code then groups games of the same category together and displays them. But yeah it doesn't limit the number of games from each category like I want.
Here is exactly what the structure of the table looks like: i49.tinypic.com/aysoll.png
Here is a blog post which sounds like what I am trying to do: http://www.e-nformation.net/content/view/title/MySQL+Top+N+in+each+group+(group+inner+limit) But I can't make sense of it.
Any help is appreciated.
How about this?
SELECT * FROM (
SELECT
games.*,
#rn := CASE WHEN #category=category THEN #rn + 1 ELSE 1 END AS rn,
#category := category
FROM games, (SELECT #rn := 0, #category := NULL) AS vars
WHERE status = 'game_published' AND featured = '1'
ORDER BY category
) AS T1
WHERE rn <= 12
you could use UNION, if we are not talking about million of types...
pseudoSQL:
(SELECT * FROM table WHERE condition AND category = 'action' ORDER BY id LIMIT 10)
UNION
(SELECT * FROM table WHERE condition AND category = 'action' ORDER BY id LIMIT 10)
UNION
(SELECT * FROM table WHERE condition AND category = 'action' ORDER BY id LIMIT 10)
If you have array of categories in your PHP/ASP, you can generate this union on the fly.
More:
http://dev.mysql.com/doc/refman/5.0/en/union.html
EDIT:
Here's probably most useful resource: http://www.xaprb.com/blog/2006/12/07/how-to-select-the-firstleastmax-row-per-group-in-sql/
Use it well ^^
There may be a more elegant solution, but you can just execute a query for each category. First get a list of categories:
SELECT DISTINCT(category) FROM `games`;
Then take each of the results and query for 12 rows:
SELECT * FROM games WHERE status = 'game_published'
AND `featured` = '1' AND `category` = $category LIMIT 12;
Of course you need to add some kind of ranking row (and order by it) to get the top 12.
Note: There may be a way to do this with a single query, but it escapes me at the moment.
To use the technique from the posts you mention, you need a way to order the games. They're using article date. Then they select the number of older articles for that company, and say there can't be more than three.
If your games table has an auto-increment column called id, you can select the top 10 games per category like:
SELECT *
FROM games g1
WHERE status = 'game_published'
AND featured = '1'
AND 10 >
(
SELECT COUNT(*)
FROM games g2
WHERE g2.status = 'game_published'
AND g2.featured = '1'
AND g1.category = g2.category
AND g2.id > g1.id
)
The where condition says that there can't be more than 10 rows with the same category and a higher ID.
Related
I want to make a fast table structure for news site with php and mysql. My database structure is ID, title, content, cat_ids (; separedet IDs of categories - ;5;10;15;20;), active, publish_date.
I want to make a fast query to select news from this table. Something like that:
SELECT id
FROM news
WHERE cat_ids LIKE '%;15;%'
AND active = 1
AND publish_date < NOW()
ORDER by publish_date DESC
LIMIT 0, 10
But if my table is 2-3GB the query is very slow. I need some ideas to make structure and make the select faster.
Instead of using cat_ids column, try creating a news_cats table with news_id and cat_id, and using this query:
SELECT id
FROM news JOIN news_cats ON news_id = id
WHERE cat_id = 15
AND active = 1
AND publish_date < NOW()
ORDER by publish_date DESC
LIMIT 0, 10
Some suggestions as below:
1) Create index on "active" field
2) Create index on "publish_date" field
3) Create separate table for category and news relation and remove "cat_ids" field from news table
New table might look like below:
news_category_ids
news_id
category_id
It can have multiple rows for each news_id, if news item falls in 3 categories, it will have 3 rows
Then use SQL like below:
SELECT news.id
FROM news INNER JOIN news_category_ids ON news.id = news_category_ids.news_id
WHERE 1
AND news.active = 1
AND news_category_ids.cat_id = 15
AND news.publish_date < NOW()
ORDER by news.publish_date DESC
LIMIT 0, 10
I have a MySQL table that looks like this:
id (int primary)
name (text)
rating (float)
I have a page showing rankings which looks like this:
$i = 0;
$q = mysql_query("SELECT * FROM teams ORDER BY rating DESC");
while($r = mysql_fetch_assoc($q)){
$i++;
print("$i: {$r['name']}<br>");
}
This shows teams in order of their rating, with a ranking. And it works.
Now, if I'm given the ID of a team, how do I find their ranking without running through the loop like this? A single MySQL query which returns the team's info + a numeric ranking indicating how far down the list they would be, if I had rendered the whole list.
Thanks!
To get the ranking you can do:
SELECT COUNT(*) as ranking
FROM teams t
WHERE t.rating >= (SELECT rating FROM teams WHERE id=$ID);
To get all the relevant info too, you can do:
SELECT t.*,COUNT(*) as rank
FROM teams t
JOIN teams t2 ON t.rating<=t2.rating
WHERE t.id=4;
This joins teams to itself joining on t.rating <= t2.rating, and so you get one row for every team that has a rating higher than or equal you.
The COUNT just counts how many teams have a rating higher than or equal to you.
Note that if there's a tie this will give you the lower rank. You can change the <= to a < if you want the highest.
You can also do it this way:
select * from (
select t.*, #rank := #rank + 1 as rank
from (select #rank := 0) as r, t
order by rating desc
) as t
where id = 20
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
I have two tables
Customer (idCustomer, ecc.. ecc..)
Comment (idCustomer, idComment, ecc.. ecc..)
obviously the two table are joined together, for example
SELECT * FROM Comment AS co
JOIN Customer AS cu ON cu.idCustomer = co.idCustomer
With this I select all comment from that table associated with is Customer, but now I wanna limit the number of Comment by 2 max Comment per Customer.
The first thing I see is to use GROUP BY cu.idCustomer but it limits only 1 Comment per Customer, but I wanna 2 Comment per Customer.
How can I achieve that?
One option in MySQL is server-side variables. For example:
set #num := 0, #customer := -1;
select *
from (
select idCustomer
, commentText
, #num := if(#customer = idCustomer, #num + 1, 1)
as row_number
, #customer := idCustomer
from Comments
order by
idCustomer, PostDate desc
) as co
join Customer cu
on co.idCustomer = cu.idCustomer
where co.row_number <= 2
This version doesn't require the SET operation:
select *
from (select idCustomer
, commentText
, #num := if(#customer = idCustomer, #num + 1, 1) as row_number
, #customer = idCustomer
from Comments
JOIN(SELECT #num := 0, #customer := 1) r
order by idCustomer, PostDate desc) as co
join Customer cu on co.idCustomer = cu.idCustomer
where co.row_number <= 2
SELECT * FROM Comments AS cm1
LEFT JOIN Comments AS cm2 ON cm1.idCustomer = cm2.idCustomer
LEFT JOIN Customer AS cu ON cm1.idCustomer = cu.idCustomer
WHERE cm1.idComment != cm2.idComment
GROUP BY cm1.idCustomer
However, if you are going to change the number of comments it's better to use Andomar's solution.
There is no need to use cursor, which is very slow. See my answer to Complicated SQL Query About Joining And Limitting. DENSE_RANK will do the trick without all cursor intricacies.
If you are using a scripting language such as PHP to process the results, you could limit the number of results shown per customer after running the query. Set up an array to hold all the results, set up another array to hold the number of results per customer and stop adding the query results to the result set after the count exceeds your limit like so:
$RESULTS = array();
$COUNTS = array();
$limit = 2;
$query = "SELECT customer_id, customer_name, customer_comment FROM customers ORDER BY RAND()";
$request = mysql_query($query);
while ($ROW = mysql_fetch_assoc($request))
{
$c = $ROW['customer_id'];
$n = $COUNTS[$c];
if ($n<$limit)
{
$RESULTS[] = $ROW;
$COUNTS[$c]++;
}
}
This guarantees only two comments per customer will be shown pulled randomly or however you want, the rest gets thrown out. Granted you are pulling ALL the results but this is (probably) faster than doing a complex join.