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
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!
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;
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
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");
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 |
+------+--------+---------+------+---------------------+------+