Ranking SQL query - php

I'm making kind of top-ten ranking in my app, and I'm stuck in the SQL query that I'll use for that.
I have 2 tables.
The 'posts' table stores the ID of the post autor(user_id), and the post content(and, of course, the entry ID).
+----+---------+--------------+
| ID | user_id | content |
+----+---------+--------------+
| 1 | 3 | Lorem Ipsum1 |
| 2 | 6 | Lorem Ipsum2 |
| 3 | 3 | Lorem Ipsum3 |
+----+---------+--------------+
The 'likes' table, stores ID of the person who liked the post(user_id), the post ID(post_id) and the like date witch is a timestamp(like_date).
+----+---------+---------+------------+
| ID | user_id | post_id | like_date |
+----+---------+---------+------------+
| 1 | 2 | 1 | 1491484851 |
| 2 | 5 | 1 | 1491484871 |
| 3 | 11 | 2 | 1491484891 |
+----+---------+---------+------------+
Every time a user like a post, an entry is created at the 'likes' table, and if the user unlike it, I just remove the entry.
And here's the deal. I want to grab the top 10 most liked users of the last 30 days. I want the query result to be something like this
+---------+-------+
| user_id | likes |
+---------+-------+
| 3 | 2 |
| 6 | 1 |
+---------+-------+
I've already tried a tons of queries and spent a couple of hours trying to solve that, but I just cant figure out how to.

This one should do the trick
select user_id, count(*)
from posts t1
join likes t2
on t1.ID = t2.post_id
where from_unixtime(t2.like_date) >= now() - interval 30 day
group by user_id
order by count(*) desc
limit 10

Assuming you mean "post_id" not "user_id" in your expected results:
SELECT post_id, count(*) as 'likes'
FROM likes
GROUP BY post_id
WHERE like_date >= DATE(NOW() - INTERVAL 30 DAY)
ORDER By 2 desc
LIMIT 10
Everything's in the likes table, so filter recent posts (WHERE), return/order by the count and onl retrieve the first 10 rows. There are other ways to achieve this...

You don't even need a join for this:
SELECT user_id, count(*) as numlikes
FROM likes l
WHERE like_date >= unix_timestamp() - 30*24*60*60
GROUP BY user_id
ORDER BY numlikes desc
LIMIT 10;
The like_date looks like a Unix timestamp, so we should treat it as one. This gets exactly 30 days from the current time to the second. If you want to count days from midnight:
WHERE like_date >= unix_timestamp(CURDATE() - interval 30 day)
EDIT:
If it is the user_id from the post, then you would use join to get that:
SELECT p.user_id, count(*) as numlikes
FROM likes l join
posts p
on l.post_id = p.id
WHERE l.like_date >= unix_timestamp() - 30*24*60*60
GROUP BY p.user_id
ORDER BY numlikes desc
LIMIT 10;

Related

How do I get distinct rows by a column?

I have a huge number of rows that I'd like to get say, last 5 records inserted in that database from 10 different users. If the same user inserted the last 3 rows into database, we must get one row, skip the others two and move to get a row per user, until it count up to 5.
A database like that:
user_id | news_id | title
1 | 1 | foo-1
2 | 2 | foo-2
3 | 3 | foo-3
1 | 4 | baa
4 | 5 | baa0
5 | 6 | baa1
5 | 7 | baa2
6 | 8 | baa3
7 | 9 | baa4
Should return:
user_id | news_id | title
1 | 1 | foo-1
2 | 2 | foo-2
3 | 3 | foo-3
4 | 5 | baa0
5 | 6 | baa1
The current filter was done by PHP, like this:
$used = array();
while ($data = mysql_fetch_array($query)) {
$uid = $data['user_id'];
if(in_array($uid, $used))
continue;
array_push($used, $uid);
// do something with data
}
But I want to refactor it, and do the filter purely by mysql, if possible. I don't know much MySql and that's why I'm having problem to archive this...
Here's what I've tried
select DISTINCT(user_id), news_id, title from XXX
WHERE GROUP BY (news_id) DESC
LIMIT 0,5
How can I do that?
1 way you can do it is to generate a partitioned row number per user and then select 5 records where RowNumber = 1.
SELECT *
FROM
(
SELECT
d.user_id
,d.news_id
,d.title
,(#rn:= if(#uid = user_id, #rn + 1,
if(#uid:=user_id,1,1)
)
) as RowNumber
FROM
Data d
CROSS JOIN (SELECT #uid:=-1, #rn:=0) vars
ORDER BY
user_id
,news_id
) t
WHERE
t.RowNumber = 1
ORDER BY news_id
LIMIT 5;
http://rextester.com/JRIZI7402 - example to show it working
Note you can change the row order by simply changing the ORDER BY statement of the derived table so if you have a column that will signify the latest record e.g. an identity column or a datetime column you can use that, but user_id must be the first criteria to be partitioned correctly.
Do it from your query.
"SELECT * FROM table GROUP BY user_id ORDER BY news_id DESC LIMIT 5"
well, i think this will achieve what you are after.
select user_id, news_id, title from tableName
GROUP BY user_id
ORDER BY news_id DESC
LIMIT 0,5
Hope this helps!

Mysql Query SUM adding each to eachother result?

Sorry if my question makes no sense. Not sure if we can do this with mysql only. Lets say I have this query:
SELECT SUM(win) * 100 as win_profit, date, uid FROM `tips` WHERE uid = 60 AND placed = 1 GROUP by date
This would obviously get the sum of the win column each day that is in the database.
Lets say the database had:
|___win___|____date____|
| 10 | 2014-04-16 |
| 10 | 2014-04-16 |
| 10 | 2014-04-17 |
| 10 | 2014-04-18 |
| 10 | 2014-04-18 |
| 10 | 2014-04-18 |
| 10 | 2014-04-19 |
| 10 | 2014-04-19 |
| 10 | 2014-04-19 |
This would result:
20
10
30
30
How can I get it to result so each adds up, mysql query only. So the result would be:
20
30
60
90
You could get all distinct dates, and LEFT JOIN to find the sum of all values up to that date; I kept the 100 multiplier from your sample query, but you need to remove it to get a result matching your desired result.
SELECT 100 * SUM(b.win), a.date
FROM (SELECT DISTINCT date FROM `tips`) a
LEFT JOIN tips b ON a.date >= b.date
GROUP BY a.date
ORDER BY a.date
An SQLfiddle to test with.
This could be another way to do it...
SET #full_sum=0;
SELECT #full_sum+SUM(win) as win_profit, date as this_date, uid,
#full_sum:=(SELECT #full_sum+SUM(win)
FROM `testing` WHERE uid = 60
GROUP by date HAVING date=this_date)
FROM `testing` WHERE uid = 60 GROUP by date;

how to display mysql latest entries from each category

we re using CodeIgniter, we want to get 6 categories latest 10 entries by (published_date), then we will display different categories results in view. here we are using 6 colourful boxes to display 10 latest entries from each category. our SQL tables are look like this ...
-> ci_categories
cat_id | cat_name | cate_slug | cate_title
-> ci_pages
page_id | cat_id | page_title | published_date
--------------------------------------
1 | 1 | Ttl 1 | 2014-02-22 10:22:20
2 | 2 | Ttl 2 | 2014-02-24 11:42:30
3 | 1 | Ttl 3 | 2014-02-26 10:37:21
4 | 3 | Ttl 3 | 2014-02-28 12:40:30
SELECT `ci_pages`.`cat_id` AS CAT_ID,
(SELECT `cat_name` FROM `ci_categories` WHERE `ci_categories`.`cat_id` = `ci_pages`.`cat_id`) AS CAT_NAME,
(SELECT `cat_slug` FROM `ci_categories` WHERE `ci_categories`.`cat_id` = `ci_pages`.`cat_id`) AS CAT_SLUG,
(SELECT `cat_title` FROM `ci_categories` WHERE `ci_categories`.`cat_id` = `ci_pages`.`cat_id`) AS CAT_TITLE
FROM `ci_pages`
ORDER BY `ci_pages`.`published_date` DESC
LIMIT 10;
This will return the 10 last inserted values into "ci_pages" Table getting each Category's details from "ci_pages" table. Check now...
try this query..
select category.*,pages.* from ci_categories category join ci_pages pages on (category.cat_id = pages.cat_id) group by pages.cat_id order_by pages.published_date DESC limit 0,10

combine two tables in mysql for recent activity functionality

In my page I have a tab named Recent activity, In which I have to display the two different types of actions
Recently added choices
Recently voted choices
The table stucture for je_addchoice
je_addpoll table
poll_id | user_id | poll_name | category_id | start_date | end_date
1 | 20 |Naturalflrs| 18 | 2012-12-03 | 2095-12-25
je_addchoice table
choice_id | poll_id | choice_creator_id | choice_name | choice_image | description | ctime
1 | 1 | 20 | Greenish | forest.jpg | forest |135453
je_user_vote table
vote_id | user_id | poll_id | choice_id | datetime_voted | user_type
12 | 31 | 1 | 1 |12-12-2606:23:17| normal
Already I have the result page displays as shown below
The above result is displayed using the query
$result=mysql_query("SELECT * FROM je_addchoice, je_addpoll where je_addpoll.start_date <= '$check_date' AND je_addpoll.end_date >='$check_date' AND je_addpoll.poll_id=je_addchoice.poll_id order by je_addchoice.choicecreationtime desc");
The two tables
1) je_addpoll (Main table for polls)
2) je_addchoice (adding choices for the polls)
But what I want to do here is If any user votes for the poll, It will store into
je_user_vote table as shown above.
I want to display the recently voted choices in the same tab
Try this
use desc order for datetime_voted just before je_addchoice.choicecreationtime desc
like this.
$result=mysql_query("SELECT * FROM (SELECT P.poll_id,P.user_id,P.poll_name,P.category_id,P.start_date,P.end_date,C.choice_id,C.choice_creator_id,C.choice_name,C.choice_image,C.description,C.ctime FROM je_addchoice C, je_addpoll P where P.start_date <= '$check_date' AND P.end_date >='$check_date' AND P.poll_id=C.poll_id order by C.choicecreationtime desc) N,je_user_vote U WHERE U.poll_id=N.poll_id order by U.datetime_voted desc,N.choicecreationtime desc");
OR
$result=mysql_query("SELECT N.poll_id,N.user_id,N.poll_name,N.category_id,N.start_date,N.end_date,N.choice_id,N.choice_creator_id,N.choice_name,N.choice_image,N.description,N.ctime FROM (SELECT P.poll_id,P.user_id,P.poll_name,P.category_id,P.start_date,P.end_date,C.choice_id,C.choice_creator_id,C.choice_name,C.choice_image,C.description,C.ctime FROM je_addchoice C, je_addpoll P where P.start_date <= '$check_date' AND P.end_date >='$check_date' AND P.poll_id=C.poll_id order by C.choicecreationtime desc) N,je_user_vote U WHERE U.poll_id=N.poll_id order by U.datetime_voted desc,N.choicecreationtime desc");

How can make sort by votes "by date", "by year"..?

What I am trying to implement is similar to what we have on SO. I want to rank posts by upvotes in last day, last month etc. My schema makes up two tables,
post(id, post, posted_on..)
vote(post_id, vote_value, date)
I hope the schema is pretty self explanatory. The problem being, if I sort "by day" by making a inner join on posts and vote and having a where clause('votes.date >= DATE_SUB(CURDATE(), INTERVAL 1 DAY'), it does work as intended but fails to show the other posts. I mean the posts which haven't had vote in last day are completely ignored. What I want is that those posts be given low priority but do show up in the query.
While, I may think of using union operation but i was looking for another approach.
Update: Lets say, there are two posts, 1,2.
and votes table is like,
post_id vote_value date
1 1 2012-12-19
2 1 2012-12-10
If I query, as per my approach, then only the post - "1" will show up since I have put a date constraint but I want both to show up. Here is my query:
SELECT `id`, SUM(`votes`.`votes`) AS likes_t, `post`.* FROM `posts` JOIN `votes` ON (`id` = `votes`.`post_id`) WHERE `votes`.`date` >= DATE_SUB(CURDATE(), INTERVAL 2 DAY)
If you want to show all posts, but only count the recent votes, this should do it:
SELECT `id`,
SUM(IF(`votes`.`date` >= DATE_SUB(CURDATE(), INTERVAL 2 DAY, `votes`.`votes`, 0)) AS likes_t,
`post`.*
FROM `posts` JOIN `votes` ON (`id` = `votes`.`post_id`)
If I got it right:
SELECT *, IF(vote.date>=DATE_SUB(CURDATE(), INTERVAL 1 DAY), 1, 0) as rate FROM post INNER JOIN vote ON (post.id=vote.post_id) ORDER BY rate DESC;
+------+--------+---------+------+---------------------+------+
| id | post | post_id | vote | date | rate |
+------+--------+---------+------+---------------------+------+
| 1 | first | 1 | 1 | 2012-12-19 00:00:00 | 1 |
| 1 | first | 1 | 1 | 2012-12-13 00:00:00 | 0 |
| 2 | second | 2 | 1 | 2012-12-10 00:00:00 | 0 |
+------+--------+---------+------+---------------------+------+

Categories