Most recent X posts by unique authors - mysql

I have a blog where multiple authors can post. On the frontpage I want to show a carousel of the most recent X posts - let's say 5. However, I want to filter out author duplicates. So if an author would have multiple posts within those 5, only the most recent one would make it in and the others would be filtered out. Also, I want to select the entire row, not just IDs. Here an example:
Data
ID | Title | AuthorID | Date
1 | "Now" | 6 | 2013-03-27
2 | "this" | 5 | 2013-03-26
3 | "is" | 4 | 2013-03-27
4 | "the" | 2 | 2013-03-28
5 | "story" | 2 | 2013-03-29
6 | "all" | 4 | 2013-04-01
7 | "about" | 2 | 2013-04-02
8 | "how" | 3 | 2013-04-03
9 | "My" | 1 | 2013-04-04
10 | "life" | 1 | 2013-04-05
Desired result
ID | Title | AuthorID | Date
10 | "life" | 1 | 2013-04-05
8 | "how" | 3 | 2013-04-03
7 | "about" | 2 | 2013-04-02
6 | "all" | 4 | 2013-04-01
2 | "this" | 5 | 2013-03-26
I currently use the following:
SELECT * FROM posts
ORDER BY date DESC
LIMIT 5
But of course this gets me the following result
ID | Title | AuthorID | Date
10 | "life" | 1 | 2013-04-05
9 | "My" | 1 | 2013-04-04
8 | "how" | 3 | 2013-04-03
7 | "about" | 2 | 2013-04-02
6 | "all" | 4 | 2013-04-01
and I want to eliminate the duplicates. How?
I saw that there is a DISTINCT clause but this would select only the author IDs and I want to select the entire row.

This trick/hack will work, but might take some time to generate results(took me 3.713 seconds on a table with 1,00,000 rows):
SELECT *
FROM (SELECT *
FROM posts
ORDER BY date DESC ) d
GROUP BY d.`AuthorID`
LIMIT 5;

I think this is what you're looking for:
SELECT *
FROM (SELECT *
FROM posts
ORDER BY Date DESC) p
GROUP BY p.AuthorID
ORDER BY Date DESC
LIMIT 5;
SQL Fiddle

Related

MySQL Group By - Top 3 rows and nth row

I have a MySQL table like this:
+------+--------+--------+
| ID | UserID | Score |
+------+--------+--------+
| 1 | 3 | 12 |
| 2 | 3 | 11 |
| 3 | 3 | 12 |
| 4 | 2 | 14 |
| 5 | 4 | 8 |
| 6 | 2 | 13 |
+------+--------+--------+
From this I want to top 3 scores from entire table and a top score from particular user
+------+--------+--------+
| ID | UserID | Score |
+------+--------+--------+
| 4 | 2 | 14 |
| 6 | 2 | 13 |
| 3 | 3 | 12 |
| 5 | 4 | 8 |
+------+--------+--------+
Is this something I can get done in a single query?
Any help is appreciated
Thanks in advance!
spoiler alert ;)
(SELECT * FROM tableA ORDER BY score DESC LIMIT 3)
UNION
(SELECT * FROM tableB WHERE UserID = 4 ORDER BY score DESC LIMIT 1);
For certain definitions of "single query", sure... If you're willing to use sub queries or a union.
The best performance will probably come from two queries, but you can join them in a UNION for convenience if you wish.

mysql select latest record only for record

I have a MySQL database with the following structure:
custodian | counta | countc | countc | total | date
-------------------------------------------------------
ed | 1 | 2 | 3 | 6 | 1/1/2016
ed | 2 | 3 | 5 | 10 | 1/2/2016
ed | 2 | 3 | 6 | 11 | 1/3/2016
ed | 1 | 3 | 5 | 9 | 1/4/2016
fred | 1 | 2 | 3 | 6 | 1/1/2016
fred | 2 | 3 | 5 | 10 | 1/2/2016
fred | 2 | 3 | 6 | 11 | 1/3/2016
fred | 1 | 3 | 5 | 9 | 1/4/2016
How do I return the latest record for a custodian? I've been playing around with this condition where date >= DATE_SUB(NOW(),INTERVAL 59 MINUTE) since the table is updated hourly, but if I update the script twice in an hour, I would return more than one result per custodian.
Any advice?
You need to combine ORDER BY and LIMIT:
SELECT *
FROM yourTableName
WHERE custodian = 123
ORDER BY `date` DESC
LIMIT 1
You could try this
SELECT * FROM tbl ORDER BY date DESC LIMIT 1
The most recent date will be the first record when ordered decendingly, and limiting the select to 1 means you get only the latest record.

MySQL GROUP By Only when Two column matches

I am trying to group a record only if two of the fields repeat themselves.
I am designing a social sharing photo app. users can share, like and comment on thers photo. Each action (share, comment, like) will appear on their friends wall.
The Problem is that when a user do all the three actions, the picture appears three times instead of one with the three action on it.
Data in database is like this (activities_tb)
id | photoID | uiID | action | date
-------------------------------------------
1 | 1 | 2 | like | 01/01/2015
2 | 1 | 2 | share | 02/01/2015
3 | 1 | 4 | share | 03/01/2015
4 | 1 | 2 | comment | 04/01/2015
5 | 2 | 4 | like | 04/01/2015
6 | 2 | 2 | like | 05/01/2015
7 | 2 | 3 | share | 05/01/2015
8 | 2 | 4 | comment | 06/01/2015
8 | 3 | 3 | like | 07/01/2015
9 | 3 | 5 | like | 08/01/2015
10 | 3 | 5 | comment | 08/01/2015
The query result I want to get
id | photoID | uiID | action | date
-------------------------------------------
3 | 1 | 4 | share | 03/01/2015
4 | 1 | 2 | comment | 04/01/2015
6 | 2 | 2 | like | 05/01/2015
7 | 2 | 3 | share | 05/01/2015
8 | 2 | 4 | comment | 06/01/2015
8 | 3 | 3 | like | 07/01/2015
10 | 3 | 5 | comment | 08/01/2015
This is my statement
SELECT id, photoID, uiID, action, date
FROM activities_tb
GROUP BY photoID, uiID.
This combines all the photos by their id returning only three results
I will be glad if anyone can be of help, thank you
You can first select required ids and join on your table:
select tb.*
from activities_tb tb
join(select max(id) as id
from activities_tb
group by photoID, uiID) t on t.id = tb.id
You are looking for "SELECT DISTINCT"
SELECT DISTINCT photoID, uiID, action, date
FROM activities_tb
GROUP BY photoID, uiID.

Listing results with unique column

I want to list top 6 race records with unique holder only. I mean a holder gets in the list shouldn't be listed with his another record. I currently use the query below to list top 6 times.
mysql> select * from racerecords order by record_time asc, date asc;
+----+---------+------------+-------------+---------------------+----------+
| id | race_id | holder | record_time | date | position |
+----+---------+------------+-------------+---------------------+----------+
| 2 | 10 | Stav | 15 | 2014-08-11 19:43:49 | 1 |
| 1 | 10 | Jennifer | 15 | 2014-08-13 19:43:19 | 1 |
| 4 | 10 | Jennifer | 16 | 2014-08-02 19:44:27 | 1 |
| 5 | 10 | Osman | 17 | 2014-08-04 19:44:57 | 1 |
| 7 | 10 | Gokhan | 18 | 2014-08-15 19:45:37 | 1 |
| 3 | 10 | MotherLode | 25 | 2014-08-01 19:44:11 | 1 |
+----+---------+------------+-------------+---------------------+----------+
6 rows in set (0.00 sec)
As you can see the holder "Jennifer" is listed twice. I want mySQL to skip her after she got in the list. The result I want to be generated is:
+----+---------+------------+-------------+---------------------+----------+
| id | race_id | holder | record_time | date | position |
+----+---------+------------+-------------+---------------------+----------+
| 2 | 10 | Stav | 15 | 2014-08-11 19:43:49 | 1 |
| 1 | 10 | Jennifer | 15 | 2014-08-13 19:43:19 | 1 |
| 5 | 10 | Osman | 17 | 2014-08-04 19:44:57 | 1 |
| 7 | 10 | Gokhan | 18 | 2014-08-15 19:45:37 | 1 |
| 3 | 10 | MotherLode | 25 | 2014-08-01 19:44:11 | 1 |
+----+---------+------------+-------------+---------------------+----------+
I tried everything. GROUP BY holder generates wrong results. It gets the very first record of the holder, even though is not the best. In this table it generates an output like above because id:1 is the first record I inserted for Jennifer.
How can I generate output a result like above?
Desired result can be achieved through this query but it performance intensive. I have reproduced the result in SQLFilddle http://sqlfiddle.com/#!2/f8ee7/3
select * from racerecords
where
(HOLDER, RECORD_TIME) in (
select HOLDER,min(RECORD_TIME) from racerecords
group by HOLDER)
Seems you have missed to include the Where clause in the sub-query. Try this
select * from racerecords
where
(HOLDER, RECORD_TIME) in (
select HOLDER,min(RECORD_TIME) from racerecords where race_id =17
group by HOLDER )
And race_id =17
Order by RECORD_TIME
you should use distinct clause
SELECT DISTINCT column_name,column_name
FROM table_name;
looks this http://www.w3schools.com/sql/sql_distinct.asp

How to form a MySQL query with 2 different ORDER BY statements

I have a sort of Facebook-esque Like/Dislike system on my website and am using the following query to grab the likes + dislikes of a specific post:
SELECT DISTINCT * FROM posts WHERE cid='$cid' AND pid=".implode(" OR pid=",$pids)." ORDER BY time DESC
$pid is an array of post ids to be searching for.
zbid is the id of the user who is currently accessing the page. Now, if that user has rated (liked/disliked) the post his result should be returned first, and then after that order the results by time DESC.
How would I go about modifying the query to do this?
If the posts table has the following data:
+----+-----+------+-----+--------+------+
| id | cid | zbid | pid | rating | time |
+----+-----+------+-----+--------+------+
| 1 | 1 | 1 | 1 | 1 | 1 |
+----+-----+------+-----+--------+------+
| 2 | 1 | 2 | 1 | -1 | 4 |
+----+-----+------+-----+--------+------+
| 3 | 1 | 3 | 2 | 1 | 2 |
+----+-----+------+-----+--------+------+
| 4 | 2 | 4 | 1 | -1 | 3 |
+----+-----+------+-----+--------+------+
| 5 | 1 | 5 | 1 | 1 | 8 |
+----+-----+------+-----+--------+------+
| 6 | 1 | 6 | 1 | -1 | 7 |
+----+-----+------+-----+--------+------+
The current select statement (above) will return the following information (with $pid = array(1);):
+----+-----+------+-----+--------+------+
| id | cid | zbid | pid | rating | time |
+----+-----+------+-----+--------+------+
| 5 | 1 | 5 | 1 | 1 | 8 |
+----+-----+------+-----+--------+------+
| 6 | 1 | 6 | 1 | -1 | 7 |
+----+-----+------+-----+--------+------+
| 2 | 1 | 2 | 1 | -1 | 4 |
+----+-----+------+-----+--------+------+
| 4 | 2 | 4 | 1 | -1 | 3 |
+----+-----+------+-----+--------+------+
| 1 | 1 | 1 | 1 | 1 | 1 |
+----+-----+------+-----+--------+------+
However, if the person with zbid=4 is accessing the page, it should bump that result (if it exists) up to the top as below:
+----+-----+------+-----+--------+------+
| id | cid | zbid | pid | rating | time |
+----+-----+------+-----+--------+------+
| 4 | 2 | 4 | 1 | -1 | 3 |
+----+-----+------+-----+--------+------+
| 5 | 1 | 5 | 1 | 1 | 8 |
+----+-----+------+-----+--------+------+
| 6 | 1 | 6 | 1 | -1 | 7 |
+----+-----+------+-----+--------+------+
| 2 | 1 | 2 | 1 | -1 | 4 |
+----+-----+------+-----+--------+------+
| 1 | 1 | 1 | 1 | 1 | 1 |
+----+-----+------+-----+--------+------+
The variable $zbid is set to the user's zbid who is accessing the page.
This is a rather rough solution I could come out with, using the information you provided:
Solution 1 - The portable way
-- This query will return User's posts and give it a higher priority in ordering, via post_order field
SELECT
posts.*
,0 as post_order
FROM posts
WHERE
(cid='$cid' AND pid=".implode(" OR pid=",$pids).") AND
(zbid = $user_zbid)
UNION ALL
-- This query will return everything BUT User's posts and give it a lower priority in ordering
SELECT
posts.*
,1 as post_order
FROM posts
WHERE
(cid='$cid' AND pid=".implode(" OR pid=",$pids).") AND
(zbid <> $user_zbid)
ORDER BY
post_order -- This clause will put User's posts before the others
,time DESC
Solution 2 - The more performing way (credits to cbranch for the suggestion)
SELECT
posts.*
,IF(zbid = $user_zbid, 0, 1) as post_order
FROM posts
WHERE
(cid='$cid' AND pid=".implode(" OR pid=",$pids).")
ORDER BY
post_order -- This clause will put User's posts before the others
,time DESC
Notes
- As you may have noticed, I removed the DISTINCT from the SELECT, as I couldn't see a reason for them. Since you just extract data from a single table, you shouldn't have duplicates. Obviously, you can still add them back, but remember not to use such clause unless it's really needed.
- The second query will be very expensive to run, as it uses the "not equal to" clauses. This means it won't be using indexes, and it won't be suitable for big amounts of data. In case you have to deal with a big table, this solution will have to be reviewed.
After reviewing Diego's suggestion I came up with the following answer that has worked:
SELECT zbid,pid,rating,0 as post_order,time
FROM posts
WHERE cid='$cid'
AND (pid=".implode(" OR pid=",$pids).")
AND zbid!='$zbid'
UNION
SELECT zbid,pid,rating,1 as post_order,time
FROM posts
WHERE cid='$cid'
AND (pid=".implode(" OR pid=",$pids).")
AND zbid='$zbid'
ORDER BY
post_order DESC,
time DESC