Mysql: Getting the 2nd earliest date for each group - mysql

Desired outcome:
I want to get the 2nd earliest date for each group.
Current:
I am getting the earliest date for each group
Query:
select * from
(select * from profile_match
where match_available_on is not null
order by profile_id asc, match_available_on asc)
as P1
group by P1.profile_id
Example: Link
From the example, I want to get:
1, '2015-05-20 03:50:11'
2, '2015-05-16 03:50:09'
And I hope that the query is not hard-coded with specific profile_id (1 or 2) as my actual data got lots of profile_id. Thanks guys!

This query will give you the results you want:
select t.profile_id, min(t.match_available_on )
from profile_match t
inner join (
select profile_id, min(match_available_on) match_available_on
from profile_match
group by profile_id
) q
on t.profile_id = q.profile_id
and t.match_available_on > q.match_available_on
group by t.profile_id;
By finding the minmatch_available_on, we can then range join against that, and choose the minimum joined value. This isn't going to be amazingly performant, but it will get the job done.
updated your demo here.
It will not return a row for a profile which has only one date available, as there is no 'second earliest'.

Related

want to return latest records where results are grouped by

I'm grouping results by SERIAL_NUMBER and I'd like to display the last records for each group according to record ID DESC this what I've got so far:
SELECT * FROM
(SELECT `SERIAL_NUMBER`, `PART_NUMBER` , `POSITION` , `DUE_CAP_CHECK_DATE` , `DUE_OVERHAUL_DATE`
FROM `history_card` ORDER BY `HISTORY_ID` DESC ) AS X
GROUP BY `SERIAL_NUMBER`
But It does not return the last record, it returns the first one ;(
You are misusing the heinously confusing nonstandard MySQL extension to GROUP BY. Read this. https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html (This extension is like a talking horse. We don't wonder that it works badly. We wonder that it works at all.)
You can get the latest (largest) value of HISTORY_ID for each value of SERIAL_NUMBER from your table like this:
SELECT MAX(HISTORY_ID) FROM history_card GROUP BY SERIAL_NUMBER
Then you can use that set of HISTORY_ID values to retrieve what you want from your table.
SELECT SERIAL_NUMBER, PART_NUMBER, POSITION, DUE_CAP_CHECK_DATE, DUE_OVERHAUL_DATE
FROM history_card
WHERE HISTORY_ID
IN (SELECT MAX(HISTORY_ID) FROM history_card GROUP BY SERIAL_NUMBER)

Solving mysql query

Part 1:
Software: Mysql workbench
Objective: To find the 2nd match for a particular user
Table: Profile_match
Columns: a_profile_id, b_profile_id, a_profile_match_available_on, b_profile_match_available_on
My process:
Find all the matches for a particular user
Find all the dates of the match for that user
Find the 2nd match (id, date) for that user
My Queries:
select * from profile_match
where (a_profile_id = '*****' and a_profile_match_available_on is not null)
or (b_profile_id = '*****' and b_profile_match_available_on is not null)
order by a_profile_match_available_on asc limit 1,1;
Complication:
A particular user can be either under a_profile_id or b_profile_id, so the date he is match can be a_profile_match_available_on or b_profile_match_available_on
The 'order by' doesn't help as the required date can be either on a_profile_match_available_on or b_profile_match_available_on
As seen in the photo (after export to excel), the highlighted profile id is the user I am looking at, and the highlighted dates are the day the user receive the match.
Desired outcome:
Outcome 1: Maybe can create a new column containing all the id of people matched with that user (non-highlighted id) and another column containing the all the highlight dates
Outcome 2: Maybe just have 4 columns. 1 column is id of user, 1 column is id of the user match, 1 column is date user is match, 1 column is date where user's match is match
~~~~Illustrated by User Dharmesh Patel~~~~: fiddle
The 2nd problem is that this query is only for a particular user. How do I expand this such that I can find the 2nd match for all my users?
Part 2:
Objective:
To find the 2nd mutual like for all users
Current progress: I can find the 2nd mutual like for a particular user, but my query can't do it for all my user
Query:
select * from profile_match
where (a_profile_id = '*****'
or b_profile_id = '*****')
and (a_profile_match_status = 1 and b_profile_match_status = 1)
order by created_on asc limit 1,1;
Info: a_profile_match_status and b_profile_match_status only takes value of 0 or 1. When both is 1, it means both users like each other. Created_on is simply the date both users indicate like.
Thanks guys!
For Part 1 you can try following query:
SELECT * FROM profile_match
WHERE (a_profile_id = 1 AND a_profile_match_available_on is not null)
OR (b_profile_id = 1 AND b_profile_match_available_on is not null)
ORDER BY (case when a_profile_id=1
THEN a_profile_match_available_on
ELSE b_profile_match_available_on end
) asc limit 1,1;
check following fiddle
UPDATE
check following modified queries:
PART 1
SELECT * FROM (SELECT P1.* FROM
(SELECT
a_profile_id AS profile_id,
a_profile_match_available_on as profile_match_available_on
FROM profile_match
UNION SELECT
b_profile_id AS profile_id,
b_profile_match_available_on as profile_match_available_on
FROM profile_match) AS P1
) AS P2
GROUP BY P2.profile_id
HAVING P2.profile_id=1 AND
profile_match_available_on > MIN(profile_match_available_on);
PART 2
SELECT * FROM (SELECT P1.* FROM
(SELECT
a_profile_id AS profile_id,
a_profile_match_available_on as profile_match_available_on
FROM profile_match
UNION SELECT
b_profile_id AS profile_id,
b_profile_match_available_on as profile_match_available_on
FROM profile_match) AS P1
) AS P2
GROUP BY P2.profile_id
HAVING profile_match_available_on > MIN(profile_match_available_on);

This slow MySQL Query needs improvement

This query works and provides me with the information I need, but it is very slow: it takes 18 seconds to agregate a database of only 4,000 records.
I'm bringing it here to see if anyone has any advice on how to improve it.
SELECT COUNT( status ) AS quantity, status
FROM log_table
WHERE time_stamp
IN (SELECT MAX( time_stamp ) FROM log_table GROUP BY userid )
GROUP BY status
Here's what it does/what it needs to do in plain text:
I have a table full of logs, each log contains a "userid", "status" (integer between 1-12) and "time_stamp" (a time stamp of when the log was created). There may be many entries for a particular userid, but with a different time stamp and status. I'm trying to get the most recent status (based on time_stamp) for each userid, then count the occurrences of each most-recent status among all the users.
My initial idea was to use a sub query with GROUP BY userid, that worked fast - but that always returned the first entry for each userid, not the most recent. If I could do GROUP BY userid using time_stamp DESC to Identify which row should be the representative for the group, that would be great. But of course ORDER BY inside of group does not work.
Any suggestions?
The first thing to try is to make this an explicit join:
SELECT COUNT(status) AS quantity, status
FROM log_table join
(select lg.userid, MAX( time_stamp ) as maxts
from log_table lg
GROUP BY userid
) lgu
on lgu.userid = lg.userid and lgu.maxts = lg.time_stamp
GROUP BY status;
Another approach is to use a different where clause. This will work best if you have an index on log_table(userid, time_stamp). This approach is doing the filtering by saying "there is no timestamp bigger than this one for a given user":
SELECT COUNT(status) AS quantity, status
FROM log_table
WHERE not exists (select 1
from log_table lg2
where lgu.userid = lg.userid and lg2.time_stamp > lg.time_stamp
)
GROUP BY status;

ORDER BY date and time BEFORE GROUP BY name in mysql

i have a table like this:
name date time
tom | 2011-07-04 | 01:09:52
tom | 2011-07-04 | 01:09:52
mad | 2011-07-04 | 02:10:53
mad | 2009-06-03 | 00:01:01
i want oldest name first:
SELECT *
ORDER BY date ASC, time ASC
GROUP BY name
(->doesn't work!)
now it should give me first mad(has earlier date) then tom
but with GROUP BY name ORDER BY date ASC, time ASC gives me the newer mad first because it groups before it sorts!
again: the problem is that i can't sort by date and time before i group because GROUP BY must be before ORDER BY!
Another method:
SELECT *
FROM (
SELECT * FROM table_name
ORDER BY date ASC, time ASC
) AS sub
GROUP BY name
GROUP BY groups on the first matching result it hits. If that first matching hit happens to be the one you want then everything should work as expected.
I prefer this method as the subquery makes logical sense rather than peppering it with other conditions.
As I am not allowed to comment on user1908688's answer, here a hint for MariaDB users:
SELECT *
FROM (
SELECT *
ORDER BY date ASC, time ASC
LIMIT 18446744073709551615
) AS sub
GROUP BY sub.name
https://mariadb.com/kb/en/mariadb/why-is-order-by-in-a-from-subquery-ignored/
I think this is what you are seeking :
SELECT name, min(date)
FROM myTable
GROUP BY name
ORDER BY min(date)
For the time, you have to make a mysql date via STR_TO_DATE :
STR_TO_DATE(date + ' ' + time, '%Y-%m-%d %h:%i:%s')
So :
SELECT name, min(STR_TO_DATE(date + ' ' + time, '%Y-%m-%d %h:%i:%s'))
FROM myTable
GROUP BY name
ORDER BY min(STR_TO_DATE(date + ' ' + time, '%Y-%m-%d %h:%i:%s'))
This worked for me:
SELECT *
FROM your_table
WHERE id IN (
SELECT MAX(id)
FROM your_table
GROUP BY name
);
Use a subselect:
select name, date, time
from mytable main
where date + time = (select min(date + time) from mytable where name = main.mytable)
order by date + time;
If you wont sort by max date and group by name, you can do this query:
SELECT name,MAX(date) FROM table group by name ORDER BY name
where date may by some date or date time string. It`s response to you max value of date by each one name
Another way to solve this would be with a LEFT JOIN, which could be more efficient. I'll first start with an example that considers only the date field, as probably it is more common to store date + time in one datetime column, and I also want to keep the query simple so it's easier to understand.
So, with this particular example, if you want to show the oldest record based on the date column, and assuming that your table name is called people you can use the following query:
SELECT p.* FROM people p
LEFT JOIN people p2 ON p.name = p2.name AND p.date > p2.date
WHERE p2.date is NULL
GROUP BY p.name
What the LEFT JOIN does, is when the p.date column is at its minimum value, there will be no p2.date with a smaller value on the left join and therefore the corresponding p2.date will be NULL. So, by adding WHERE p2.date is NULL, we make sure to show only the records with the oldest date.
And similarly, if you want to show the newest record instead, you can just change the comparison operator in the LEFT JOIN:
SELECT p.* FROM people p
LEFT JOIN people p2 ON p.name = p2.name AND p.date < p2.date
WHERE p2.date is NULL
GROUP BY p.name
Now, for this particular example where date+time are separate columns, you would need to add them in some way if you want to query based on the datetime of two columns combined, for example:
SELECT p.* FROM people p
LEFT JOIN people p2 ON p.name = p2.name AND p.date + INTERVAL TIME_TO_SEC(p.time) SECOND > p2.date + INTERVAL TIME_TO_SEC(p2.time) SECOND
WHERE p2.date is NULL
GROUP BY p.name
You can read more about this (and also see some other ways to accomplish this) on the The Rows Holding the Group-wise Maximum of a Certain Column page.
I had a different variation on this question where I only had a single DATETIME field and needed a limit after a group by or distinct after sorting descending based on the datetime field, but this is what helped me:
select distinct (column) from
(select column from database.table
order by date_column DESC) as hist limit 10
In this instance with the split fields, if you can sort on a concat, then you might be able to get away with something like:
select name,date,time from
(select name from table order by concat(date,' ',time) ASC)
as sorted
Then if you wanted to limit you would simply add your limit statement to the end:
select name,date,time from
(select name from table order by concat(date,' ',time) ASC)
as sorted limit 10
In Oracle, This work for me
SELECT name, min(date), min(time)
FROM table_name
GROUP BY name
work for me mysql
select * from (SELECT number,max(date_added) as datea FROM sms_chat group by number) as sup order by datea desc
This is not the exact answer, but this might be helpful for the people looking to solve some problem with the approach of ordering row before group by in mysql.
I came to this thread, when I wanted to find the latest row(which is order by date desc but get the only one result for a particular column type, which is group by column name).
One other approach to solve such problem is to make use of aggregation.
So, we can let the query run as usual, which sorted asc and introduce new field as max(doc) as latest_doc, which will give the latest date, with grouped by the same column.
Suppose, you want to find the data of a particular column now and max aggregation cannot be done.
In general, to finding the data of a particular column, you can make use of GROUP_CONCAT aggregator, with some unique separator which can't be present in that column, like GROUP_CONCAT(string SEPARATOR ' ') as new_column, and while you're accessing it, you can split/explode the new_column field.
Again, this might not sound to everyone. I did it, and liked it as well because I had written few functions and I couldn't run subqueries. I am working on codeigniter framework for php.
Not sure of the complexity as well, may be someone can put some light on that.
Regards :)

MySQL Subquery with main query data variable

Ok, need a MySQL guru here. I am trying to write a query that will serve as a notification system for when someone leaves a comment on an item that you have previously commented on. The 'drinkComment' table is very simple:
commentID, userID, drinkID, datetime, comment
I've written a query that will get all of the comments on drinks that I have previously commented on (that are not mine), but it will still show comments that occurred BEFORE my comment. This is as close to what I would think would work, but it does not. Please help!
select #drinkID:=drinkComments.drinkID, commentID, drinkID, userID, comment, datetime
FROM drinkComments
WHERE `drinkID` IN
( select distinct drinkID from drinkComments where drinkComments.userID = 1)
AND drinkComments.dateTime > (
/*This gets the last date user commented on the main query's drinkID*/
select datetime FROM drinkComments WHERE drinkComments.userID = 1 AND drinkComments.drinkID = #drinkID ORDER BY datetime DESC LIMIT 1
)
ORDER BY datetime DESC
Why not start with a prequery of the user and all the drinks they've offered comments and as of what time (don't know if you have multiple comments per person for any given drink or not). Then, find comments from all others AFTER such of your date/time comment...
This query should actually be faster as it is STARTING with only ONE USER's drink comments as a basis, THEN goes back to the comments table for those matching the drink ID and cutoff time.
SELECT STRAIGHT_JOIN
dc.*
from
( select
drinkID,
max( datetime ) UserID_DrinkCommentTime
FROM
drinkComments
WHERE
userID = 1
group by
drinkID ) PreQuery
join DrinkComments dc
on PreQuery.DrinkID = dc.DrinkID
and dc.datetime > PreQuery.UserID_DrinkCommentTime
order by
dc.DateTime desc
I think you need to relate your innermost query to the middle query by drinkID.
select #drinkID:=drinkComments.drinkID, commentID, drinkID, userID, comment, datetime
FROM drinkComments
WHERE `drinkID` IN
( select distinct drinkID from drinkComments AS a where drinkComments.userID = 1)
AND drinkComments.dateTime > (
/*This gets the last date user commented on the main query's drinkID*/
select datetime FROM drinkComments WHERE drinkComments.userID = 1 AND drinkComments.drinkID = a.drinkID ORDER BY datetime DESC LIMIT 1
)
ORDER BY datetime DESC