My search query runs like:
select * from posts p where p.post like '%test%' ORDER BY p.upvotes DESC,
p.unix_timestamp DESC LIMIT 20
If there are more than 20 results for the searched keyword, i find out the minimum timestamp value, store it in a hidden element and run another query to Load More results like:
select * from posts p where p.post like '%test%' and p.unix_timestamp < 1360662045
ORDER BY p.upvotes DESC, p.unix_timestamp DESC LIMIT 20
Whats really happening is that my first query is ignoring (Obviously, my mistake) posts which haven't had any votes(meaning 0 votes) because of my ORDER BY p.upvotes DESC and as a result of this, i noticed that it fetched the first post in the table in the first 20 results, so the minimum timestamp becomes first post's timestamp. Now after this, if i try to fetch the next 20 results which is less than the minimum timestamp, it doesn't give anything.
Right now, i am simply using the upvotes ordering to fetch top records. Should i be using some algorithm like Bayesian Average or some other algorithm?
Please advise how i can improve the queries if i had to stay with current system of ordering or is there any viable and more efficient method i should be using?
P.S. If possible, please refer some resources about the Bayesian Average(it seems to be most used) or some other alternative?
Storing the timestamp when you first do a search and then using that for the next query you could use something like this:-
$sql = "SELECT *
FROM posts p
LEFT OUTER JOIN (SELECT post_id, COUNT(*) FROM post_ratings WHERE timestamp_rated <= $SomeTimeStoredBetweenPages GROUP BY post_id) pr ON p.id = pr.post_id
WHERE p.post like '%test%'
ORDER BY pr.post_ratings DESC, p.unix_timestamp
DESC LIMIT ".(($PageNo - 1) * 20)." 20";
This is very much an example as I have no real idea of you table structures. Also not sure if you just have a row for each up vote, or whether there are down votes to take account of as well.
Related
First I am new to SQL and PHP.
I have created a simple social networking web app so users can post and follow others to see new posts from them.
At home page a user can first see posts from all users he is followong.
but what i want is to make the user see some other random popular posts that will be ordered by Likes.
here what i have done to get posts from users i follow:
SELECT * FROM posts WHERE author_id in
(SELECT followedID FROM follows WHERE
followerID=:myID)
ORDER BY id DESC LIMIT 10
Now let's say you are following only 1 person. and that person has only one post. here you will see no more than a post!
That's why i want to show more posts when a user has already seen all posts.
i want some easy way to get other posts when the above query has done getting some specific posts.
This is the next query i'd like to execute.
SELECT * FROM posts ORDER BY post_likes DESC LIMIT 10
I wouldn't recommend union, because it incurs overhead for removing duplicates.
Instead, you can use a LEFT JOIN:
SELECT p.*
FROM posts p LEFT JOIN
follows f
ON p.author_id = f.follows_id AND
f.followerID = :myID
ORDER BY (f.follows_id IS NOT NULL) DESC,
(CASE WHEN f.follows_id IS NOT NULL THEN p.id END),
p.post_likes DESC
LIMIT 10;
The ORDER BY puts the followed posts first. The other two clauses order each of the groups by the criteria you want.
You may use UNION to do what you want
(SELECT * FROM posts WHERE author_id in
(SELECT followedID FROM follows WHERE
followerID=:myID)
ORDER BY id DESC limit 0,10)
union
(SELECT * FROM posts ORDER BY post_likes DESC limit 0,10)
LIMIT 0, 10
UNION will automatically append the 2nd query result to the 1st query result, and then show only the number of records specified by the LIMIT clause
Please note that union works only if the queries are of the same structure (which in this case is positive)
Please note that the use of parenthesis is mandatory if you use order by or limit or both
I have used 3 limit clauses (one for each query , and one for the final result of union) AND Both queries have ORDER BY clause. This is to make sure that the records extracted are what you want. (to show the followed posts first, and both are ordered properly)
I'm working on a time based game (where a lower time is better), and I have a leaderboard for it, which stores every playthrough into a SQL database.
I'd like to get the top 15 lowest (best) times from the database, but only show one output for each user, so that the best times of the top 15 users are displayed, but I can't seem to do it while returning all of the information.
The Query I'm using is:
SELECT * FROM `scores` WHERE size='$size' ORDER BY `time` ASC LIMIT 15
Thank you.
If you group your data using the user column, you can use MIN() to isolate the lowest/best time for each users. Finally, you sort by BestTime ASC (so that lower numbers are listed first) and truncate the result set with LIMIT.
Query:
SELECT `user`, MIN(`time`) AS BestTime
FROM `scores`
WHERE `size` = '10x10'
GROUP BY `user`
ORDER BY `BestTime`
LIMIT 15;
SELECT * FROM (SELECT user,size,min(time) as time FROM scores
WHERE size = '10x10'
GROUP BY user, size)
ORDER BY time
LIMIT 15
Selects minimum time for each users and returns top 15 users with their min time score.
You would appear to want something like this:
select s.*
from scores s
where s.score = (select max(s2.score) from scores s2 where s2.userid = s.userid)
order by s.score asc
limit 15;
I have no idea what size is for in your sample query.
I have two tables in the mysql database:
trips(id,name,desc,createdat)
trip_ratings(id,ratingvalue,tripid,userid)
I want to get 100 most recent trips sorted by higher average ratings.
I have tried following mysql query:
SELECT AVG(ratingvalue),tripid,tripcreatedat FROM trip_ratings
INNER JOIN trips on trip_ratings.tripid = trips.id
GROUP BY trip_ratings.tripid
ORDER BY AVG(ratingvalue) DESC, tripcreatedat DESC
LIMIT 100
But as it sorts by rating value first I only get trips sorted by higher ratings.
Is it possible within a single query? can anyone hint me what should I do?
EDIT: example:
I have data in trip_ratings table like this:
and from my tried query I can get results like this.
But my problem is: Get 100 most recent trips sorted by higher average ratings.
Instead of downvoting and close votes, can anyone have any solution for that or can anyone give me a hint that is it possible within a single query? thanks.
One option is to use an inline view, to first get the "100 most recent trips".
Then join that to trip_ratings, to calculate the "average rating" and order by that result.
SELECT m.id AS tripid
, AVG(r.ratingvalue) AS average_rating
, m.tripcreatedat
FROM ( SELECT t.id
, t.tripcreatedat
FROM trips t
ORDER BY t.tripcreatedat DESC
LIMIT 100
) m
LEFT
JOIN trip_ratings r
ON r.tripid = m.id
GROUP BY m.id, m.tripcreatedat
ORDER BY AVG(r.ratingvalue) DESC
If there are multiple trips that have the same average rating, it's indeterminiate what order those will be returned in. You can add other expressions to the order by to make it more deterministic.
ORDER BY AVG(r.ratingvalue) DESC, m.tripcreatedat DESC, m.id DESC
This isn't the only way to do it. There are other approaches that will achieve an equivalent result.
I've got following tables in my MySQL database:
USERS
iduser nick password
ARTICLES
idarticles iduser text
How can I get by one SQL query the list of e.g. 10 top publishers of articles ordering by the count of added articles? Is there any way to do that? I'm using PHP to ask database.
Yes, this should be quite easy via JOIN and COUNT(). Something like the following
SELECT `users`.`iduser`, COUNT(`articles`.`idarticles`) AS `total_articles`
FROM `users`
INNER JOIN `articles` ON `users`.`iduser` = `articles`.`iduser`
GROUP BY `users`.`iduser`
ORDER BY `total_articles` DESC
LIMIT 10
A little explaining:
COUNT() will get you what it says - a count of all relevant entries in articles
INNER JOIN will pair all entries that belong together (defined via ON)
GROUP BY tells SQL that you are interested in various results, each differing by iduser (without this, you'd get all articles counted in the first returned row)
ORDER BY .. DESC is important to get the result in a descending order (so most published first)
LIMIT 10 does exactly that
As I am trying to count the number of records in a table, even when the SQL statement has a LIMIT into it, overall it works, however something weird happens, the code:
$sql = "SELECT COUNT(*) AS count FROM posts
ORDER BY post_date DESC
LIMIT 5";
// ... mysql_query, etc
while($row = mysql_fetch_array($result))
{
// ... HTML elements, etc
echo $row['post_title'];
// ... HTML elements, etc
echo $row['count']; // this displays the number of posts (which shows "12").
}
Although, when displaying through the while loop, it displays this:
Notice: Undefined index: post_title in /Applications/MAMP/htdocs/blog/index.php on line 55
If I remove the COUNT(*) AS count, everything will display perfectly... how come it's doing this?
Don't use COUNT(*) to count the number of rows (for a lot of reasons). Write out your full query, and add SQL_CALC_FOUND_ROWS right after SELECT:
SELECT SQL_CALC_FOUND_ROWS id, title FROM foo LIMIT 5;
Then, after that query executed (right after), run:
SELECT FOUND_ROWS();
That will return the number of rows the original SELECT would have returned if you didn't have the LIMIT on the end (accounting for all joins and where clauses).
It's not portable, but it's very efficient (and IMHO the right way of handling this type of problem).
LIMIT does not do anything here because you're selecting a single scalar. The error is shown because you are not selecting the post title, so it is not in the $row hash.
This is happening because COUNT() is an aggregate function. You will have to do two separate queries in order to get both the count of rows in the table and separate records.
I had the same problem and found this article: http://www.mysqldiary.com/limited-select-count/
best way (especially for big tables) is using it like this:
SELECT COUNT(*) AS count FROM (SELECT 1 FROM posts
ORDER BY post_date DESC
LIMIT 5) t
now it returns a number between 0 and 5
You're asking the SELECT statement to return only COUNT(*) AS count. If you want more columns returned, you have to specify them in the SELECT statement.
You're mixing up an aggregate function COUNT() with a standard selection. You can't get an accurate post title without a GROUP BY clause in your aggregate query. The easiest thing you should do is to do this two queries - one for your count, the other for your post information. Also, there's no sense in using LIMIT on an aggregate function with no other columns in your (omitted) GROUP BY - the current query will always return one row.
What everyone is trying to say is that you should opt to use 2 separate queries instead of 1 single one.
1) get row count of table
$sql = "SELECT COUNT(*) AS count FROM posts;
2) get last 5 rows of table
$sql = "SELECT * FROM posts
ORDER BY post_date DESC
LIMIT 5";
By including COUNT(*) AS count you've removed the actual columns that you want to get the information from. Try using
SELECT *, COUNT(*) AS count FROM posts ORDER BY post_date DESC LIMIT 5
That query will accomplish both things you're trying to do in one query, but really, these should be broken out into 2 separate queries. What is the overall purpose for having a COUNT in the query? It should be done a different way.
This works for me:
$sql = "
SELECT COUNT( po.id ) AS count, p.post_title
FROM posts p
JOIN posts po
GROUP BY p.post_title
ORDER BY p.post_date DESC
LIMIT 5
";
Important is to JOIN the same table, but name the table differently. Do not forget GROUP BY, must be used.