How to SELECT MySQL 1-to-LAST on 1-to-Many Relationship - mysql

I have 2 Tables -- Table 1 is a master file and Table 2 is an activity file.
The relationship is 1-to-Many.
I am producing a report and all I want to return is every master file row joined to only the last activity row for the related master id.
I am unsure on how to request the last activity row. My code below returns every activity row (rightfully so).
Thank you for your help.
SELECT *
FROM master_file AS master
INNER JOIN activity_file AS activity ON activity.id = master.id
ORDER BY master.display_name
The activity file has a column called entry_date. It is a date and time stamp recording every activity. I simply want to select the last entry_date.
For example:
Table 2 - Activity looks like this
ID ACTIVITY ENTRY_DATE
1 Update 2012-08-01 09:00:00
1 Edit 2012-08-01 13:45:15
3 Create 2012-07-15 10:09:52
3 Delete 2012-07-22 23:02:00
3 Add 2012-08-05 04:33:00
4 Edit 2012-08-03 15:12:00

One standard way to solve this is to create an inline view that finds the last entry_date per ID.
SELECT *
FROM master_file AS master
LEFT JOIN activity_file AS activity ON activity.id = master.id
LEFT JOIN (select id , max(entry_date) entry_date
From activity_file
group by id) last_activity
ON activity_file.id = last_activity.id
and activity_file.entry_date= last_activity.entry_date
ORDER BY master.display_name
The one problem with this approach is that you may have more than one record with max(entry_date) for a given id. You'll either need to have your business rules handle this (e.g. simply display more than one record for that case) or you'll need to figure out a tie breaker. The last thing you want is to make it non-deterministic

Related

How do I do a MySQL JOIN on conditions based on mathematical operations across two tables?

I have two tables in a CRM application that I am trying to build.
"Contacts" Table:
id
Name
ContactFrequency (in days)
1
John
7
2
Pete
30
"Events" Table:
id
Contacts_id
Description
Unix_Timestamp
1
1
Sent John an email
1609667504
2
1
Gave John a call
1609645455
1
2
Sent Pete a letterl
1609666755
The "ContactFrequency" is how often I should call that client to stay in touch. Each entry in Events is a call log with a UNIX timestamp. I want to generate a list of clients who need to be called in the next X number of days (or whose next contacts have already passed and are overdue), such as the following (timestamps are completely arbitrary in my examples):
Client
Next Contact Due on (Timestamp)(Ordered by this column)
Pete
1609645352
John
1609634342
How would I do this with a query? I can't wrap my head around it.
Formally:
SELECT Contacts.Name Client,
COALESCE(FROM_UNIXTIME(MAX(Events.Unix_Timestamp)) + INTERVAL Contacts.ContactFrequency DAY, CURRENT_TIMESTAMP) NextContactDueOn
FROM Contacts
LEFT JOIN Events ON Contacts.id = Events.Contacts_id
GROUP BY Client
ORDER BY NextContactDueOn DESC

how to select specific rows in some condition

I tried to write a query that selects rows with steps that both user 1 and user 2 did, with combined number of times they did the step (i.e., if user 1 did step 1 3 times and user 2 did 1 time then the count should show 4 times.)
when I put condition as user_id=1, user_id=2 there is no error but it return nothing, when it should return some rows with values.
there is table step, and step taken
and table step has column id, title
table step_taken has column id, user_id(who performs steps), step_id
i want to find step that both of two user whose id 1,2 did
and also want to have the value as count added up how many times they performed that step.
for example if user id 1 did step named meditation 2 times,
and user id 2 did step named meditation 3 times,
the result i want to find should be like below ;
------------------------------
title | number_of_times
------------------------------
meditation| 5
------------------------------
here is my sql query
select title, count(step_taken.step_id)as number_of_times
from step join step_taken
on step.id = step_taken.step_id
where user_id = 1 and user_id=2
group by title;
it returns nothing, but it should return some rows of step both user1 and user 2 did.
when i wrote same thing only with user_id=1 or user_id=2, it shows selected information
how can I fix my code so it can show the information I want to get?
thanks in advance :)
user_id cannot be 1 and 2 at the same time. You need a second user table. Then join those on your criteria and count:
select title, count(u1.id) + count(u2.id) as number_of_times
from step u1 join step u2
on u1.id = u2.id
where u1.user_id = 1 and u2.user_id=2
group by title;
note: cannot tell what table title is in, or the purpose of step_taken was as step.id is identical.

Joins are coming back with no rows selected

i'm having some trouble with trying to extract some data from several MySQL tables in a join statement.
My tables and attributes are:
appointment_end_time (table)
appointment_end_time_id (int)(pk)(ai)
appointment_end_date (datetime)
appointment_start_time (table)
appointment_date_id (int)(pk)(ai)
appointment_start_date (datetime)
instructor(table)
instructor_id (int)(pk)(ai)
firstname varchar(45)
lastname varchar(45)
appointment_timetable
appointment_timetable_id int(11) AI PK
instructor_id int(11) FK
appointment_date_id int(11) FK
appointment_end_time_id int(11) FK
SELECT a.appointment_timetable_id, i.instructor_id, ad.appointment_start_date, aet.appointment_end_date
FROM db12405956.appointment_timetable a
JOIN instructor i on i.instructor_id = a.instructor_id
JOIN appointment_start_time ad on ad.appointment_date_id = a.appointment_date_id
JOIN appointment_end_time aet on aet.appointment_end_time_id = a.appointment_end_time_id
ORDER BY a.appointment_timetable_id;
However, this code brings back no rows selected when executed so i'm wondering what i'm doing wrong, any help will be much appreciated
Sample rows:
(appointment_end_time)
appointment_end_time_id appointment_end_date
1 2016-12-26 14:00:00
2 2016-12-24 13:00:00
3 2016-12-26 13:00:00
(appointment_start_time)
appointment_date_id appointment_start_date
1 2016-12-26 15:00:00
2 2016-12-24 16:00:00
3 2016-12-26 15:30:00
instructor_id firstname lastname
1 Sasha Thompson
2 Laura Robinson
3 John Walters
appointment_timetable
appointment_timetable_id instructor_id appointment_date_id appointment_end_time_
1 Blank Blank Blank
2 Blank Blank Blank
3 Blank Blank Blank
What you need is to learn how to diagnose the problem yourself. It is a common problem that a query doesn't return the expected results and you should understand how to break things down to find the issue.
Let's start with your query:
SELECT a.appointment_timetable_id, i.instructor_id, ad.appointment_start_date, aet.appointment_end_date
FROM db12405956.appointment_timetable a
JOIN instructor i on i.instructor_id = a.instructor_id
JOIN appointment_start_time ad on ad.appointment_date_id = a.appointment_date_id
JOIN appointment_end_time aet on aet.appointment_end_time_id = a.appointment_end_time_id
ORDER BY a.appointment_timetable_id;
What you do to break it down is start with the first table and then add the joins (and where conditions although you don't have any here), one at a time until the data problem appears. I find this easiest to do by using select * or select top 1 * (Or top 10 as I usually prefer to see more than one record) instead of the field list because then you don't have to look for the fields that are associated with joins you haven't added in yet.
So start with
SELECT top 10 *
FROM db12405956.appointment_timetable a
Then try
SELECT top 10 *
FROM db12405956.appointment_timetable a
JOIN instructor i on i.instructor_id = a.instructor_id
Then
SELECT top 10 *
FROM db12405956.appointment_timetable a
JOIN instructor i on i.instructor_id = a.instructor_id
JOIN appointment_start_time ad on ad.appointment_date_id = a.appointment_date_id
Finally
SELECT top 10 *
FROM db12405956.appointment_timetable a
JOIN instructor i on i.instructor_id = a.instructor_id
JOIN appointment_start_time ad on ad.appointment_date_id = a.appointment_date_id
JOIN appointment_end_time aet on aet.appointment_end_time_id = a.appointment_end_time_id
ORDER BY a.appointment_timetable_id;
At some point you will see where the records fell out and that is the location of the problem. Then you might need to look at the fields you are joining on and the data in them in your data sets to see why they are not returning any matches. For instance, if you are joining on dates, they may be stored as dates in one table and as varchar in another and date "01/01/2016' is not equal to 'Jan 1, 2016' or sometimes the column has some sort of prefix or suffix not in the other table. Something like PR2345 in one table and 2345 in the other. Sometimes the query is correct and no rows genuinely meet the conditions. This could be because the data is not fully populated yet (think writing a report for a system that is not live yet, no data on completed actions because none have completed yet.) or because the requirement was wrong in some of its assumptions or because there should be no matching records. It could even be a bug in the data entry.
Depending on the nature of the problem, you might need to return all the records or only use select top 1 (since all records are disappearing). Using SELECT * this way will help when you are returning too many or duplicate records as well as sometimes is is the fields not being returning that affect the results set. Note that I am not saying to use SELECT * in your final result set, it is only being used as a diagnostic tool here.
In your case, the problem looks as if it is in the first table. There are blanks for instructor ID and the other fields in your sample, so there is nothing to join on. (You only gave a sample so the rest of the table may not be like this.) If this is a case where the data is not there yet due to the feature that would add it not yet being live, then you can test your query only by adding test data to the table. Be sure to delete this data after you have finished unit testing. If the data should have been there, then you need to look at the insert from the application for a bug.

mysql highly selective query

I have a data set like this:
User Date Status
Eric 1/1/2015 4
Eric 2/1/2015 2
Eric 3/1/2015 4
Mike 1/1/2015 4
Mike 2/1/2015 4
Mike 3/1/2015 2
I'm trying to write a query in which I will retrieve users whose MOST RECENT transaction status is a 4. If it's not a 4 I don't want to see that user in the results. This dataset could have 2 potential results, one for Eric and one for Mike. However, Mike's most recent transaction was not a 4, therefore:
The return result would be:
User Date Status
Eric 3/1/2015 4
As this record is the only record for Eric that has a 4 as his latest transaction date.
Here's what I've tried so far:
SELECT
user, MAX(date) as dates, status
FROM
orders
GROUP BY
status,
user
This would get me to a unqiue record for every user for every status type. This would be a subquery, and the parent query would look like:
SELECT
user, dates, status
WHERE
status = 4
GROUP BY
user
However, this is clearly flawed as I don't want status = 4 records IF their most recent record is not a 4. I only want status = 4 when the latest date is a 4. Any thoughts?
SELECT user, date
, actualOrders.status
FROM (
SELECT user, MAX(date) as date
FROM orders
GROUP BY user) AS lastOrderDates
INNER JOIN orders AS actualOrders USING (user, date)
WHERE actualOrders.status = 4
;
-- Since USING is being used, there is not a need to specify source of the
-- user and date fields in the SELECT clause; however, if an ON clause was
-- used instead, either table could be used as the source of those fields.
Also, you may want to rethink the field names used if it is not too late and user and date are both found here.
SELECT user, date, status FROM
(
SELECT user, MAX(date) as date, status FROM orders GROUP BY user
)
WHERE status = 4
The easiest way is to include your order table a second time in a subquery in your from clause in order to retrieve the last date for each user. Then you can add a where clause to match the most recent date per user, and finally filter on the status.
select orders.*
from orders,
(
select ord_user, max(ord_date) ord_date
from orders
group by ord_user
) latestdate
where orders.ord_status = 4
and orders.ord_user = latestdate.ord_user
and orders.ord_date = latestdate.ord_date
Another option is to use the over partition clause:
Oracle SQL query: Retrieve latest values per group based on time
Regards,

How to combine two rows with similar timestamps and return both

I have a MySQL table of bouts set up like this.
|------------------------|
|bouts |
|------------------------|
|boutID |
|recording_athlete |
|boutdate (timestamp) |
|opponent |
|recording_athlete_points|
|------------------------|
Each actual meeting between two people is recorded twice in the table, with a unique boutID and boutdate (reflecting the moment when it was actually entered, but within 5 minutes of the other) and the recording athlete of one is the opponent of the other, and visa versa. The two records are not necessarily consecutive. There are additional meetings for the two participants each day, separated by longer time intervals: we're looking for the two closest in both timestamp and ID number (assuming that these are the two that belong together).
I'm trying to SELECT records that belong together into one row (and realize and want it will be done twice) such that it will output matched rows something like this:
boutID|recording_athlete|boutdate|opponent|recording_athlete_points|boutID_b|boutdate_b|opponent_points
01|John|2012-05-10 20:33:04|Jane|15|04|2012-05-10 20:36:12|10
04|Jane|2012-05-10 20:36:12|John|10|01|2012-05-10 20:33:04|15
Here is what I have so far, and where I think I need to go, but just can't figure out what to use. Some sort of interval statement? Or do I need a totally different structure?
SELECT
A.`boutID`,
A.`recording_athlete`,
A.`boutDate`,
A.`opponent`,
A.`recording_athlete_points`,
B.`boutID` as `boutID_b`,
B.`boutDate` as `boutdate_b`,
B.`recording_athlete_points`as `opponent_points`
FROM bouts A
INNER JOIN bouts B on(A.`fullName` = B.`opponent` AND ????? )
ORDER by A.`boutDate`
SELECT
...
FROM bouts A
JOIN bouts B
on A.fullName = B.opponent
AND B.boutdate between subdate(A.boutdate, interval 5 minute)
and adddate(A.boutdate, interval 5 minute)
If you create an index on boutdate:
create index bouts_boutdate_index on bouts(boutdate);
this query will perform well