I have a database with two tables, Users and Posts. I'd like to write a php script to list, in order, the top 20 usernames with the number of posts they've made. The username field is cUsername in the Users table. The Users table intUserID field and the Posts table's intPosterID field correspond to eachother.
Any help would be much appreciated. Thanks!
Use GROUP BY and count. This will get you a list of user IDs to their counts:
SELECT intPosterId, COUNT(*)
FROM Posts
GROUP BY intPosterId
ORDER BY COUNT(*) DESC
LIMIT 20
You can use the result in a subquery:
SELECT u.cUsername, pcnt.postCount
FROM Users AS u
INNER JOIN (
SELECT intPosterId, COUNT(*) as postCount
FROM Posts
GROUP BY intPosterId
ORDER BY COUNT(*) DESC
LIMIT 20
) AS pcnt
ON u.intUserId = pcnt.intPosterId
To use it in a PHP script:
<?php
$sql = '
SELECT u.cUsername, pcnt.postCount
FROM Users AS u
INNER JOIN (
SELECT intPosterId, COUNT(*) as postCount
FROM Posts
GROUP BY intPosterId
ORDER BY COUNT(*) DESC
LIMIT 20
) AS pcnt
ON u.intUserId = pcnt.intPosterId
';
$pdo = new PDO(
'mysql:host=your_host;dbname=your_db',
'username',
'password',
array(PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION)
);
$stmt = $pdo->prepare($sql);
$stmt->execute();
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo "{$row['cUsername']}: {$row['postCount']} <br />\n";
}
Related
I have a news site and I am trying to include a block of news in it.
see the image please
I created a category call world news and added subcategories. (Travel, News, Art, Bussines)
I display them one by one, I mean I do a query for each news, that means 4 different queries like below :
$sql = "SELECT posts.post_catId,
posts.post_seo_url,
posts.post_desc,
posts.post_type,
posts.post_status,
posts.post_title,
posts.post_image_url,
categories.catId,
categories.catName,
categories.cat_seo_url
FROM posts
LEFT JOIN categories
ON posts.post_catId = categories.catId
WHERE post_catId = catId AND cat_seo_url = 'art'
AND post_status = ?
ORDER BY post_created_at DESC LIMIT 1";
$stmt = $pdo->prepare($sql);
$stmt->execute(['1',]);
if($stmt->rowCount() > 0){
while($row = $stmt->fetch()){
//here
}
}
My question is: is there a way to display them in one query ?
Edit : I want to display 1 news from 4 specific categories with one query instead of 4.
I know I can add a new row to categories table to secify which categories can be displayed.
You 'invert' the query, selecting the categories first and then joining to the posts table, constraining it by the post_seo_url field, like so:
$sql = "SELECT
categories.catId,
categories.catName,
categories.cat_seo_url,
posts.post_catId,
posts.post_seo_url,
posts.post_desc,
posts.post_type,
posts.post_status,
posts.post_title,
posts.post_image_url,
FROM categories
JOIN posts ON posts.post_seo_url = (
SELECT p.post_seo_url FROM posts as p
WHERE categories.catId = p.post_catId
ORDER BY p.post_created_at DESC LIMIT 1
)
WHERE post_status = ?"
Here's a method using ROW_NUMBER() function:
SELECT p.*,
categories.catId,
categories.catName,
categories.cat_seo_url
FROM
(SELECT
ROW_NUMBER() OVER (PARTITION BY posts.post_catId ORDER BY posts.post_created_at DESC) AS RN,
posts.post_catId,
posts.post_seo_url,
posts.post_desc,
posts.post_type,
posts.post_status,
posts.post_title,
posts.post_image_url
FROM posts
WHERE post_status = ? ) p
JOIN categories
ON p.post_catId = categories.catId
WHERE RN=1;
I've made the query on posts table to become a subquery with addition of ROW_NUMBER() function then JOIN it with categories table and added WHERE that only return row number = 1. I've changed the LEFT JOIN to JOIN because I don't see any reason to use LEFT JOIN in this operation. However, if there is a reason, please update it in your question.
Here's a demo fiddle
I have this query:
$q = "SELECT * FROM user
WHERE sec='1' AND reg_by='".$_SESSION['login_username']."'
ORDER BY date DESC LIMIT $startrow, 30 ";
I have another table which stores appointments, it has a column named meet.
How can I sort this query by meet?
Not all data at users are in other table.
You can use the below query. Replace another_table with your original table name :
$q = "SELECT u.* FROM user AS u LEFT JOIN another_table AS at ON u.userid = at.userid WHERE u.sec='1' AND u.reg_by='".$_SESSION['login_username']."' ORDER BY at.meet DESC LIMIT $startrow, 30 ";
you can use joining for this like
select user.*,meet.* from user left join meet on (meet.userid = user.id) where user.sec='1' AND user.reg_by='".$_SESSION['login_username']."' order by meet.userid DESC
$q = "SELECT * FROM user INNER JOIN user
ON meets.userid=user.userid WHERE sec='1' AND reg_by='".$_SESSION['login_username']."' ORDER BY date DESC LIMIT $startrow, 30 ";
I have a working PHP script that selects the content with a join of a user table. Then I while loop the results.
However, I was wondering if it would be possible to instead of calling another query each loop, call it in the initial query? Basically merging them?
Code:
$content_result = mysql_query("
SELECT content.id, content.type, content.title, content.url, users.username
FROM content
INNER JOIN users ON content.uploaderuid = users.id
ORDER BY content.id DESC
LIMIT $offset, $rowsperpage
");
while($content_row = mysql_fetch_array($content_result)){
$contentid = $content_row['id'];
$result_num_votes = mysql_fetch_assoc(mysql_query("SELECT COUNT(*) FROM votes WHERE votedcontentid = '$contentid' AND value = '1'"));
$num_votes = $result_num_votes['COUNT(*)'];
}
Try this:
SELECT COUNT(votedcontentid)as numbervotes,content.id, content.type, content.title, content.url, users.username
FROM content
INNER JOIN users ON content.uploaderuid = users.id
JOIN votes ON votes.votedcontentid = content.id
ORDER BY content.id DESC LIMIT $offset, $rowsperpage
And here change it to
$num_votes = $result_num_votes['numbervotes'];
I'm currently working on a news database website and I can't seem to create a query to select the 5 hottest news articles. The 2 tables of the database that are affected for this query are:
News - containing all news items (id, author, message, etc.)
Rates - containing all ratings on news items (id, news_id, rating, etc.)
Now my query should select 5 news_ids from the table Rates with the highest average rating and most votes ( so: ordered by AVG(Rating) and COUNT(*) I supposed ). I first tried to make my query as well get all info of these news_ids from the News table instantly ( using a WHERE id IN(--the query selecting the 5 hottest news_ids--) clause ) but that returned an error of my MySql Version not being cappable of using LIMIT inside of the WHERE IN clause sub-query.
Well, I hope you can help me out on the first query that has to select those 5 news_ids. The query I got as for now ( but not fully working ) is:
SELECT news_id FROM
(SELECT news_id, AVG(rating) AS average_r, COUNT(*) AS amt_r
FROM rates
GROUP BY news_id
ORDER BY average_r,amt_r
DESC LIMIT 5
) AS news_rates
or in content with the rest of my script:
$get_hot_news_ids = mysql_query("SELECT news_id FROM
(SELECT news_id, AVG(rating) AS average_r, COUNT(*) AS amt_r
FROM rates
GROUP BY news_id
ORDER BY average_r,amt_r DESC LIMIT 5) AS news_rates");
$first = 1;
while($news_id = mysql_fetch_assoc($get_hot_news_ids)) {
if(!$first) {
$hot_news_ids .= " ,";
}else{
$first = 0;
}
$hot_news_ids .= $news_id['news_id'];
}
//print_r($hot_news_ids);
$get_hot_news = mysql_query("SELECT * FROM news
WHERE id IN($hot_news_ids)
ORDER BY FIELD(id, $hot_news_ids)");
Are you sure both average_r and amt_r are both in descending order?
SELECT news_id FROM
(SELECT news_id, AVG(rating) AS average_r, COUNT(*) AS amt_r
FROM rates
GROUP BY news_id
ORDER BY average_r DESC, amt_r DESC
LIMIT 5
) AS news_rates
Try this:
SELECT TOP 5 N.id, N.author, N.message, AVG(R.rating) AS rate, COUNT(R.news_id) AS votes
FROM news N
INNER JOIN rates R ON N.id = R.news_id
GROUP BY N.id, N.author, N.message
ORDER BY rate, votes
You can use a join instead, which will allow the limit:
SELECT *
FROM news n JOIN (
SELECT news_id, AVG(rating) AS average_r, COUNT(*) AS amt_r
FROM rates
GROUP BY news_id
ORDER BY average_r,amt_r DESC
LIMIT 5
) top5 ON n.news_id = top5.news_id
ORDER BY top5.average_r,top5.amt_r
Note: You might want to change your query to a ORDER BY average_r DESC, amt_r DESC to get the highest rated items, instead of the lowest rated.
I'm trying to accomplish:
selecting top 20 highest score users with the least SQL / PHP way possible.
caching is still not been considered for now.
What I've done so far:
I'm able to retrieve all 5k+ records with their scores, but not able to limit to only retrieve or calculate top 20(example).
Tables:
users (id, name)
score_rec (id, uid, points) This table has multiple entries for each user. Highest scores will be the ones which has highest amounts of rows, entries. Example: UID 23 could have 5 rows which belong to it, his score is 5.
Code Sample:
$query = "SELECT * FROM score_rec,users where users.id = score_rec.uid";
$result = mysql_query($query);
$array1 = Array();
while($row = mysql_fetch_array($result) )
{
//Count their scores
$query2 = "SELECT users.id,users.name,score_rec.uid FROM `score_rec`,`users` where score_rec.uid = $row[uid] and users.id = $row[uid]";
$result2 = mysql_query($query2);
$scores_count = mysql_num_rows($result2);
$array1["$row[name]"] = $scores_count;
}
I'm thinking this might be possible with maybe a temporary table script, stored procedure, or simple query which could look at both tables. Since scores_rec could be used by itself to calculate higuest entries holders, maybe one query could suffice to both tables.
Thank you all for any direction given.
What about something like this :
select users.id, count(*) as score
from users
inner join score_rec on users.id = score_rec.uid
group by users.id
order by count(*) desc
limit 20
This will :
For each user, count how many rows he has (because of the group by)
sort all users by number of rows -- in descending order
keep the first 20 resulting rows -- which are the 20 users who have the bigger number of rows
$query = "
SELECT users.id,users.name,count(*) score
FROM score_rec
INNER JOIN users on users.id = score_rec.uid
GROUP BY users.id,users.name
ORDER BY score DESC
LIMIT 20
";
$result = mysql_query($query);
$array1 = Array();
while($row = mysql_fetch_array($result) )
{
$array1["$row[name]"] = $row['score'];
}
Depending on how you want your scores:
1) global top 20 scores, possibly repeating users:
SELECT users.id, users.name, score_rec.points
FROM users
LEFT JOIN score_rec ON users.id = score_rec.uid
ORDER BY score_rec.points DESC;
2) scores of the top 20 distinct players:
SELECT DISTINCT users.id, ...
etc...