GROUP BY with ORDER BY / last post_time - sql

I have a list of writers in a topic. The List shows the writers name a the last post time inside this topic.
A member can also write more frequently in the topic. Therefore a "group by" was set on the poster_id.
The whole list should be sorted according to the write date. The newest comes first. So I placed maxtime DESC.
Problem:
The list output is working very well but the date of a writer's last post is not the last post_time but always the first post_time.
Table "USERS":
user_id
username
1
Marc
2
Paul
3
Sofie
4
Julia
Table "POSTS"
post_id
topic_id
poster_id
post_time
4565
6
1
999092051
4567
6
4
999094056
4333
6
2
999098058
7644
6
1
999090055
This is my query:
SELECT
p.poster_id,
p.post_time,
p.post_id,
Max(p.post_time) AS maxtime,
u.user_id,
u.username,
FROM POSTS as p
INNER JOIN USERS as u ON u.user_id = p.poster_id
WHERE p.topic_id = 6
GROUP BY p.poster_id
ORDER BY maxtime DESC
How is it possible to display the last post_time of the poster_id instead the first one.

Using columns in Select which are not in group by or in an aggregation function is in most db's forbiden, becuase it is not defined which values are shown.
You can use a subquery with group by and having
SELECT
p.poster_id,
p.post_time,
p.post_id,
p.topic_id,
u.user_id,
u.username
FROM posts as p
INNER JOIN users as u ON u.user_id = p.poster_id
where p.topic_id = 6 AND (p.poster_id, p.post_time )in(select poster_id, max(post_time) from posts group by poster_id )
Demo

The problem with your query is that all your non-aggregated fields must be present inside the GROUP BY clause. As long as this condition is not met, you either have an error fired by your DBMS, or expect some subtle output mistakes. Additionally your query would fire a syntax error due to the comma after the last selected field in the SELECT clause.
If you're using MySQL 8.0, you can use the ROW_NUMBER window function to select your last post time for each poster (rownum = 1, partitioned by poster, ordered by post_time desc), then join back to your users table to get information.
WITH topic_id6 AS (
SELECT poster_id, post_time, post_id, topic_id,
ROW_NUMBER() OVER(PARTITION BY topic_id, poster_id ORDER BY post_time DESC) AS rn
FROM POSTS
)
SELECT id6.poster_id, id6.post_time, id6.post_id, id6.topic_id, u.*
FROM topic_id6 id6
INNER JOIN USERS u
ON id6.poster_id = u.user_id
AND id6.rn = 1
Check the demo here.

Related

postgreSQL doesn't order by

I have this postgreSQL query, which is supposed to be
returning
top 3 comments ordered by comment_date from
top 10
posts table ordered by post_date...
for some reason it's not ordering by post_date:
with a as
(
SELECT
posts.post_id as post_id
FROM posts where user_id = 'user1'
order by post_date desc --this is not working
limit 10
), b as
(
select user_id,
comment_id,
post_id,
comment_date
, ROW_NUMBER() over (partition by post_id order by comment_date desc) as RowNum
from post_comments
)
SELECT * from a
INNER JOIN b USING (post_id)
where b.RowNum <= 3
example: https://extendsclass.com/postgresql/434e9d2
in the example it should be getting the post which has the highest post_date so like this post3>post2>post1
but the ordering of posts is not working...
I'm new to postgreSQL so i don't have any idea what's happening... actually i got that query from a stackoverflow answer just edited it a little bit: Answer
Thanks for answering and sorry for my bad english
The ordering is just fine. How do I know? The outer query has no order by. Without an order by the results can be in any order.
If you want results in a particular order, then you need to be explicit:
SELECT *
FROM a JOIN
b USING (post_id)
WHERE b.RowNum <= 3
ORDER BY a.post_date DESC;
Note: You'll need to include post_date in the a CTE.

How can order by last replies?

I have two tables the first is the 'users' the second is the 'posts',
the 'users' have two columns:
1 id
2 username
the 'posts' have five columns:
1 p_id
2 uid
3 post_id
4 content
5 date
the posts as defined have the value '0' in post_id and the replies have the value p_id in post_id. My query is
SELECT id,username,p_id,uid,post_id,content,date
FROM users
inner join posts
ON users.id=posts.uid
WHERE post_id='0'
ORDER BY p_id DESC
but I want to order by the last replies like in a forum.
Use a correlated subquery in the SELECT clause to find the latest reply date and order by that column. If the post has no replys, use the date of the post instead:
SELECT id,username,p_id,uid,post_id,content,date, (
SELECT MAX(date) FROM posts r WHERE r.post_id = p.p_id
) AS last_reply_date
FROM users
inner join posts p
ON users.id=p.uid
WHERE p.post_id='0'
ORDER BY COALESCE(last_reply_date, p.date) DESC

Get max with one count

I use a sql request to get the number message by user by forum.
SELECT count(idpost), iduser, idforum
FROM post
group by iduser, idforum
And I get this result:
But I want to get the better poster in one forum. How can I get it?
I want to get the user who has the most number of post in ONE forum like this :
According to the edited question:
Please try the query below. What we need is a subquery finding max(idpost) based on idforum. Think the query below:
select max(idpost) as IDPOST,idforum
from post
group by idforum
What this query is supposed to do is to find the number of posts by a user on a forum. So it should present you an output like:
idpost idforum
3 1
2 2
3 4
Then we need to find the related iduser for this rows as:
select p2.IDPOST, p1.iduser, p2.idforum
from post p1 inner join
( --the query above comes here as subquery.
select max(idpost) as IDPOST,idforum
from post
group by idforum
) p2 on p1.idforum = p2.idforum and p1.idpost = p2.IDPOST
What it is doing is to match the data from your main table with the temprorary data coming from your subquery based on idforum and idpost values and adding iduser value from your original table.
idpost iduser idforum
3 2 1
2 6 2
3 2 4
Well assuming you supply the idForum of the forum in question.........just get the top row of your query ordered by the count desc
SELECT TOP 1 * FROM
(
SELECT count(idpost),iduser,idforum FROM post
GROUP BY iduser,idforum
) PostsCount
WHERE PostsCount.idForum = #theForumIdIamLookingFor
ORDER BY count DESC
SELECT MAX(CN),idforum,Max(iduser) iduser
FROM (
SELECT count(idpost) CN,iduser,idforum
FROM post
Group By iduser,idforum
) A Group By idforum
If your DBMS supports RANK/ROW_NUMBER it simple:
select cnt, iduser, idforum
from
(
SELECT count(idpost) as cnt, iduser, idforum,
RANK() OVER (PARTITION BY idforum ORDER BY count(idpost) DESC) as rnk
FROM post
group by iduser, idforum
) dt
where rnk = 1
This might return more than one row per forum if multiple users share the same count. Switch to ROW_NUMBER if you want to return only one (but random) row.
SELECT count(idpost) MAX,iduser,idforum FROM post
ORDER BY count(idpost) DESC group by iduser,idforum
Or, you can do a nested query. But, I prefer this.
You can easily pick the first result as MAX

Select rows where the last row of associated table has a specific value

I have two tables:
User (id, name)
UserEvent (id, user_id, name, date)
How can I get all the users where the last (ordered by date) UserEvent.name has a value of 'played'?
I wrote an example on SQLFiddle with some specific data: http://sqlfiddle.com/#!9/b76e24 - For this scenario I would just get 'Mery' from table User, because even though 'John' has associated events name of the last one is not 'played'.
This is probably fastest:
SELECT u.*
FROM usr u -- avoiding "User" as table name
JOIN LATERAL (
SELECT name
FROM userevent
WHERE user_id = u.id
ORDER BY date DESC NULLS LAST
LIMIT 1
) ue ON ue.name = 'played';
LATERAL requires Postgres 9.3+:
What is the difference between LATERAL and a subquery in PostgreSQL?
Or you could use DISTINCT ON (faster for few rows per user):
SELECT u.*
FROM usr u -- avoiding "User" as table name
JOIN (
SELECT DISTINCT ON (user_id)
user_id, name
FROM userevent
ORDER BY user_id, date DESC NULLS LAST
) ue ON ue.user_id = u.id
AND ue.name = 'played';
Details for DISTINCT ON:
Select first row in each GROUP BY group?
SQL Fiddle with valid test case.
If date is defined NOT NULL, you don't need NULLS LAST. (Neither in the index below.)
PostgreSQL sort by datetime asc, null first?
Key to read performance for both but especially the first query is a matching multicolumn index:
CREATE INDEX userevent_foo_idx ON userevent (user_id, date DESC NULLS LAST, name);
Optimize GROUP BY query to retrieve latest record per user
Aside: Never use reserved words as identifiers.
Return the max date from user event grouping by user id. Take that result set and join it back to user event by the user id and max date and filter for just the played records.
Here it is:
First i get the MAX ID from each user and join it to the ROW with this ID
to test if the status are 'played' if so, i get the username of them.
SELECT
ids.*,
u.name,
ue.*
FROM (
SELECt max(id) AS id from UserEvent
GROUP by user_id
) as ids
LEFT JOIN UserEvent ue ON ue.id = ids.id
LEFT JOIN User u ON u.id = ue.user_id
WHERE ue.name = 'played';

Highest Record for a set user

Hope someone can help.
I have been trying a few queries but I do not seem to be getting the desired result.
I need to identify the highest ‘’claimed’’ users within my table without discarding the columns from the final report.
The user can have more than one record in the table, however the data will be completely different as only the user will match.
The below query only provides me the count per user without giving me the details.
SELECT User, count (*) total_record
FROM mytable
GROUP BY User
ORDER BY count(*) desc
Table:
mytable
Column 1 = User Column 2 = Ref Number Column 3 = Date
The first column will be the unique identifier, however the data in the other columns will differ, therefore it needs to descend the highest claimed user with all the relevant rows to the user to the least claimed user.
User|Ref Num|Date
1|a|20150317
1|b|20150317
2|c|20150317
3|d|20150317
4|e|20150317
1|f|20150317
4|e|20150317
The below data is how the values should be returned.
User|Ref Num|Date|Count
1|a|20150317|3
1|b|20150317|3
1|f|20150317|3
2|c|20150317|1
3|d|20150317|1
4|e|20150317|2
4|e|20150317|2
Hope it makes sense.
Thank you
As you're using MSSQL you can use the OVER() clause like so:
SELECT [user], mt.ref_num, mt.[date], COUNT(mt.[user]) OVER(PARTITION BY mt.[user])
FROM myTable mt
More about the OVER clause can be found here: https://msdn.microsoft.com/en-us/library/ms189461.aspx
As per your comment you can use the wildcard * like so:
SELECT mt.*, COUNT(mt.[user]) OVER(PARTITION BY mt.[user])
FROM myTable mt
This would get you every column as well as the result of the count.
If you want to order by the number of record for each user, then use window functions instead of aggregation:
SELECT t.*
FROM (SELECT t., count(*) OVER (partition by user) as cnt
FROM mytable t
) t
ORDER BY cnt DESC, user;
Note that I added user to the order by so users with the same count will appear together in the list.
You could use an outer apply if your version of SQL Server supports it:
SELECT [User], [Ref Num], Date, total_record
FROM mytable M
OUTER APPLY (
SELECT count(*) total_record
FROM mytable
WHERE [user] = M.[user]
GROUP BY [user]
) oa
ORDER BY total_record desc, [user]
Note that user is a reserved keyword in MSSQL and you need to enclose it in either brackets [user] or double-quotes "user".
This would produce an output like:
user Ref Num Date total_record
1 a 2015-03-17 3
1 b 2015-03-17 3
1 f 2015-03-17 3
4 e 2015-03-17 2
4 e 2015-03-17 2
2 c 2015-03-17 1
3 d 2015-03-17 1
Note that the answers using the count(*) OVER (partition by [user]) construct are more efficient though.
Most simple way would be to use window fuction.
SELECT table.*, COUNT(*) OVER (PARTITION BY user)
FROM nameoftable table -- this is an alias
ORDER BY user, ref_num
This also seem to fit your need.
This is the old way of doing it. Where possible you should use OVER but as other people have answered with that I thought I'd throw this one into the mix.
SELECT
T.[User]
,T.[Ref Num]
,T.[Date]
,(SELECT count(*) from [myTable] T2 where T2.[User] = T.[USER]) as [Count]
FROM [mytable] T
ORDER BY [Count] DESC