Home > Enterprise >  SQL query for listing values based on a column
SQL query for listing values based on a column

Time:01-12

I have a table with the following columns member_id, status and created_at (timestamp) and i want to extract the latest status for each member_id based on the timestamp value.

member_id status created_at
1 ON 1641862225
1 OFF 1641862272
2 OFF 1641862397
3 OFF 1641862401
3 ON 1641862402

Source: Raw data image

So, my ideal query result would be like this:

member_id status created_at
1 OFF 1641862272
2 OFF 1641862397
3 ON 1641862402

Expected query results image

CodePudding user response:

My go to process for doing things like that is to assign a row number to each data and get row number 1 depending on the partition and sorting.

For mysql, this is only available starting mysql 8

SELECT ROW_NUMBER() OVER(PARTITION BY member_id ORDER BY created_at DESC) as row_num, 
member_id, status, created_at FROM table

This will generate something like this.

row_num member_id status created_at
1 1 OFF 1641862272
2 1 ON 1641862225
1 2 OFF 1641862397
1 3 ON 1641862402
2 3 OFF 1641862401

Then you use that as a sub query and get the rows where row_num = 1

SELECT member_id, status, created_at FROM (
    SELECT ROW_NUMBER() OVER(PARTITION BY member_id ORDER BY created_at DESC) as row_num, 
    member_id, status, created_at FROM table
) a WHERE row_num = 1

CodePudding user response:

MySQL has support for Window Function since v8.0. the solution from crimson589 is preferred for v8 , this solution applies for earlier versions of MySQL or if you need an alternate solution to window queries.

After grouping by member_id we can either join back into the original set to gain the corresponding status value to the MAX(created_at)

SELECT ByMember.member_id
, status.status
, ByMember.created_at
FROM (
  SELECT member_id, max(created_at) as created_at
  FROM MemberStatus
  GROUP BY member_id
  ) ByMember
JOIN MemberStatus status ON ByMember.member_id = status.member_id AND ByMember.created_at = status.created_at;

Or you could use a sub query instead of the join:

SELECT ByMember.member_id
, (SELECT status.status FROM MemberStatus status WHERE ByMember.member_id = status.member_id AND ByMember.created_at = status.created_at) as status
, ByMember.created_at
FROM (
  SELECT member_id, max(created_at) as created_at
  FROM MemberStatus
  GROUP BY member_id
  ) ByMember

The JOIN based solution allows you to query additional columns from the original set instead of having multiple sub-queries. I would almost always advocate for the JOIN solution, but sometimes the sub-query is simpler to maintain.

I've setup a fiddle to compare these options: http://sqlfiddle.com/#!9/0edb931/11

CodePudding user response:

You can group by member_id and max of created_at, then a self join with member_id and created_at will give you the latest status.

  •  Tags:  
  • Related