MySQL query with GROUP BY and ORDER BY timestamp DESC - mysql

I am saving the history of Facebook likes for a page, identified by user_id.
Now from this table, I need to get a set representing the user_id's and their latest number of likes, based on the most recent timestamp.
I started off with this:
SELECT *
FROM facebook_log
GROUP BY user_id
ORDER BY timestamp DESC;
But that does not do what I want because it returns the first records with the lowest timestamps.
I read something online about GROUP returning the very first records from the table.
I also understood something about JOIN the table with itself, but that doesn't work either, or I did something wrong.

If you just need the user_id and the timestamp, you can just do
select f.user_id, max(f.timestamp)
from facebook_log
group by user_id;
if you need all the data from the table, you can do
select f.*
from facebook_log f
inner join (select max(timestamp) mt, user_id
from facebook_log
group by user_id) m
on m.user_id = f.user_id and m.mt = f.timestamp

You can also get the latest number of likes by using this MySQL trick:
select f.user_id, max(f.timestamp),
substring_index(group_concat(f.numlikes order by f.timestamp desc), ',', 1) as LatestLikes
from facebook_log f
group by f.user_id;

Related

How to select single rows using MAX and GROUP BY on non-uniqe field in MySQL?

I came across this very simple case where I need to select a list of conversations from Conversations table along with latest message from Messages table - which has non-uniqe dateCreated field.
After long research I came up with this query:
SELECT
Conversations.id,
dateCreated,
`name`,
lastMessageId,
lastMessageDate,
lastMessagePayload
FROM Conversations
LEFT JOIN (
SELECT
id AS lastMessageId,
m1.conversationId,
payload AS lastMessagePayload,
m1.dateCreated AS lastMessageDate,
FROM Messages AS m1
INNER JOIN (
SELECT conversationId, MAX(dateCreated) AS mdate FROM Messages GROUP BY conversationId
) AS m2
ON m1.conversationId = m2.conversationId AND m1.dateCreated = m2.mdate
) AS msg2
ON msg2.conversationId = Conversations.id
ORDER BY dateCreated DESC
Query works well but if two latest messages in same conversation have exact same dateCreated field this query would then output two conversations with same id but different lastMessage... row of fields.
I just couldn't find a way to get around this problem as main problem is when you do GROUP BY a field and MAX on another non-uniqe field then you can't get out always only one row out.
Any idea how to get list of unique conversations with latest message (any message of the two if they have the same date)?
Use row_number()!
select c.*, m.* -- or whatever columns you want
from conversations c left join
(select m.*,
row_number() over (partition by m.conversationid order by m.dateCreated desc, m.id desc) as seqnum
from messages m
) m
on m.conversation_id = c.id and
m.seqnum = 1;
MySQL 5.x version...
Use a correlated sub-query to get the latest message id (for a given conversation), using ORDER BY and LIMIT 1
SELECT
Conversations.Conversations.id,
Conversations.dateCreated,
Conversations.`name`,
Messages.id AS lastMessageId,
Messages.payload AS lastMessagePayload,
Messages.dateCreated AS lastMessageDate,
FROM
Conversations
LEFT JOIN
Messages
ON Messages.id = (
SELECT lookup.id
FROM Messages AS lookup
WHERE lookup.conversationId = Conversations.id
ORDER BY lookup.dateCreated DESC
LIMIT 1
)
ORDER BY
Conversations.dateCreated DESC
In the event of two messages having the same date, the message you get is non-deterministic / arbitrary.
You could, if you wanted, therefore change it to get the highest id from the most recent date...
ORDER BY lookup.dateCreated DESC, lookup.id DESC
LIMIT 1

Order rows by amount of columns in another table

I'm currently outputting all of my members by adding the MySQL clause ORDER BY id DESC, but I feel that doesn't reward people that are active on my service.
I thought about judging the order by the amount of entries in another table they have under their ID.
Essentially, I'm asking if it's possible to order columns in a MAIN table counting the amount of rows where the users ID is in the column of the row.
Something pseudo to this
SELECT user_id,name,etc FROM users ORDER BY (
COUNT(SELECT FROM users_interactions WHERE user_id = user_id) *******
) ASC
In the end of the COUNT statement, the user_id = user_id was just a guess.
You are almost there - what you need to do is to put COUNT inside SELECT:
SELECT user_id,name,etc FROM users u ORDER BY (
SELECT COUNT(*)
FROM users_interactions i
WHERE i.user_id = u.user_id
) ASC
You could also do it using a JOIN, like this:
SELECT u.user_id, u.name, u.etc
FROM users u
LEFT OUTER JOIN users_interactions i ON i.user_id = u.user_id
GROUP BY u.user_id, u.name, u.etc
ORDER BY COUNT(*) ASC

MYSQL Query of four tables

I'm stuck doing a query. I'd like to extract the 10 first records of one table considering the values of the other three tables. I'll try to explain what I want with an example:
TABLES
USERS: username and date
POINTS: id_user, points
COMMENTS: id_user
WON: id_user
THE CRITERIA MUST BE: The 10 users who have more points and, in case of equal values, with more comments published, less recent date date and didn't won. In that order.
SELECT id, username, date FROM users as us LIMIT 10 ORDER BY date DESC JOIN id_user, points FROM points as po WHERE us.id = po.id_user ORDER BY po.points DESC JOIN COUNT (id_user) FROM comments JOIN COUNT (id_user) FROM won;
I know that's wrong... :(
Assuming Users has an id field and a created datetime field, I think you're looking for something like this
SELECT *
FROM Users
LEFT JOIN Points ON Points.id_user = Users.id
LEFT JOIN Comments ON Comments.id_user = Users.id
LEFT JOIN Won ON Won.id_user = Users.id
GROUP BY Users.id
ORDER BY SUM(Points.points) DESC, COUNT(Comments.id) DESC, MAX(Users.created) DESC, COUNT(Won.id) DESC

Ordering JOIN results when grouping

Take the below for example:
SELECT
*
FROM
auctions AS a
LEFT JOIN winners AS w ON a.auction_id=w.auction_id
WHERE
a.active=1
AND
a.approved=1
GROUP BY
a.auction_id
ORDER BY
a.start_time DESC
LIMIT
0, 10;
Sometimes this may match multiple results in the winners table; I don't need both of them, however I want to have control over which row I get if there are multiple matches. How can I do an ORDER BY on the winners table so that I can make sure the row I want is the first one?
It is difficult to accurately answer without seeing your table structure but if your winners table has a winner date column or something similar, then you can use an aggregate function to get the first record.
Then you can return the record with that earliest date similar to this:
SELECT *
FROM auctions AS a
LEFT JOIN winners w1
ON a.auction_id=w1.auction_id
LEFT JOIN
(
select auction_id, min(winner_date) MinDate -- replace this with the date column in winners
from winners
group by auction_id
) AS w2
ON a.auction_id=w2.auction_id
and w1.winner_date = w2.MinDate
WHERE a.active=1
AND a.approved=1
ORDER BY a.start_time DESC
SELECT *
FROM auctions AS a
LEFT JOIN (select auction_id from winners order BY auction_id limit 1) AS w ON a.auction_id = w.auction_id
WHERE a.active = 1
AND a.approved = 1
GROUP BY a.auction_id
ORDER BY a.start_time DESC
Change the reference to the winners table in the join clause to a sub-query. This then gives you control over the number of records returned, and in what order.

MySQL query already GROUPed and ORDERed : how to ORDER inside the GROUPs?

I have this query:
SELECT id_user, COUNT(*) as count
FROM posts
GROUP BY id_user
ORDER BY COUNT(*) DESC
which gives me the id_user ordered by occurrences, and the number of each occurrence.
Can I get, in the same request, the LAST post from each 'id_user'? i.e. I want to select the last 'post' too, but when I do
SELECT id_user, post, COUNT(*) as count
Tthe value in 'post' isn't the last one (nor the first one; actually I don't know how groups are ordered). Should I run another query?
I believe u can accomplish this by adding max(post_id) last_post to your select.
This ought to do it in one query:
SELECT
p.id_user,
ap.post AS last_post,
COUNT(*) as count
FROM
posts p
JOIN posts ap on (
p.id_user = ap.id_user
AND ap.post_id = (
SELECT MAX(post_id) FROM posts ip WHERE p.id_user = ip.id_user
)
GROUP BY
p.id_user,
ap.post
ORDER BY
COUNT(*) DESC