I have a training_stats table (current due training) and I also have a completed_training table.
What I want to do is query due training with the last completed date from the completed table.
I've nearly got what I want, I get the due training, but they are duplicated with each completed record(as there are many completed records to each current due), and I only want single rows and the latest completed date.
I've been trying to use MAX, and when I run the MAX query independently, I get the last record. But when the MAX query is in the join, it is returning all completed rows.
This is the query that I am using:
SELECT s.course_stat_id
,o.org_name
,u.id
,u.first_name
,u.last_name
,a.area_id
,a.area_name
,tc.course_id
,tc.course_name
,s.assigned_on
,s.due
,s.pass_mark
,s.completed_on
,completed.complete_training_id
,completed.complete_date
FROM training_stats s
JOIN organisations o ON o.org_id = s.org_id
LEFT JOIN (
SELECT complete_training_id
,user_id
,area_id
,course_id
,max(completed_on) AS complete_date
FROM completed_training
GROUP BY complete_training_id
) completed ON completed.user_id = s.user_id
AND completed.area_id = s.area_id
AND completed.course_id = s.course_id
LEFT JOIN users u ON u.id = s.user_id
LEFT JOIN areas a ON a.area_id = s.area_id
LEFT JOIN training_courses tc ON tc.course_id = s.course_id
WHERE u.active = 1
AND o.active = 1
AND s.assigned = 1
Can you see what I am doing wrong?
Not exactly positive of your expected results, but the failure is PROBABLY for your group by and JOIN. Your group by is ONLY on the training ID, but you are also pulling user, area and course as well as max date completed for said respective training ID, user, area, course. You group by and join should match the unique characteristics.
Without seeing data, the query as I interpret it is that the "complete_training_id" is an auto-increment column for that table. Having said that, there would only ever be one record for that ID.
Having said that, the completed training table can have for a single user, area and course, multiple training days of which you want the most recent. For example someone attending college and needs to take many computer classes and they are refreshers from prior so assume all are same course ID. A person could take in 2012, 2014, 2016. You would want the instance of the user/area/course showing the 2016 dated training. So lets look at that first.
select
ct.user_id,
ct.area_id,
ct.course_id,
max(ct.completed_on) AS complete_date
FROM
completed_training ct
GROUP BY
ct.user_id,
ct.area_id,
ct.course_id
Now, for each user, area and course of study, I have one record with the most recent completion date. NOW lets pull the rest of the details, but since you need the completed training ID too, I applied the MAX() of that in the query below. The ID should by default be increasing every time a new record is added, so one completed a year ago would have a lower value than the ID completed today. So you get both the completed ID and its corresponding date for a given user, area, course.
SELECT
s.course_stat_id,
o.org_name,
u.id,
u.first_name,
u.last_name,
a.area_id,
a.area_name,
tc.course_id,
tc.course_name,
s.assigned_on,
s.due,
s.pass_mark,
s.completed_on,
ct.complete_training_id,
ct.complete_date
FROM
training_stats s
JOIN organisations o
ON s.org_id = o.org_id
AND o.active = 1
LEFT JOIN
( select
ct.user_id,
ct.area_id,
ct.course_id,
max(ct.complete_training_id ) as complete_training_id,
max(ct.completed_on) AS complete_date
FROM
completed_training ct
GROUP BY
ct.user_id,
ct.area_id,
ct.course_id ) ct
on s.user_id = ct.user_id
AND s.area_id = ct.area_id
AND s.course_id = ct.course_id
JOIN users u
ON s.user_id = u.id
AND u.active = 1
LEFT JOIN areas a
ON s.area_id = a.area_id
LEFT JOIN training_courses tc
ON s.course_id = tc.course_id
WHERE
s.assigned = 1
I'm not 100% sure of that. First, run this query. It should list all completed training, with a rnk from 1 (lastest), to n (oldest).
SELECT complete_training_id
,user_id
,area_id
,course_id
,completed_on AS complete_date
,#curRank := case when complete_training_id <> #cur_complete_training_id then 0 else #curRank + 1 end rnk
FROM completed_training, (select #curRank := 0, #cur_complete_training_id := 0)
ORDER BY complete_training_id, completed_on DESC
If true, the answer is :
SELECT s.course_stat_id
,o.org_name
,u.id
,u.first_name
,u.last_name
,a.area_id
,a.area_name
,tc.course_id
,tc.course_name
,s.assigned_on
,s.due
,s.pass_mark
,s.completed_on
,completed.complete_training_id
,completed.complete_date
FROM training_stats s
JOIN organisations o ON o.org_id = s.org_id
LEFT JOIN (
SELECT complete_training_id
,user_id
,area_id
,course_id
,completed_on AS complete_date
,#curRank := case when complete_training_id <> #cur_complete_training_id then 0 else #curRank + 1 end rnk
FROM completed_training, (select #curRank := 0, #cur_complete_training_id := 0)
ORDER BY complete_training_id, completed_on DESC
) completed ON completed.user_id = s.user_id and completed.rnk = 1
AND completed.area_id = s.area_id
AND completed.course_id = s.course_id
LEFT JOIN users u ON u.id = s.user_id
LEFT JOIN areas a ON a.area_id = s.area_id
LEFT JOIN training_courses tc ON tc.course_id = s.course_id
WHERE u.active = 1
AND o.active = 1
AND s.assigned = 1
Related
I have 400 rows every tables. So, I will try to relationship every tables using LEFT JOIN based on ID, but my query takes 15 seconds, and this is my query:
SELECT
sender.id AS id,
sender.letter AS letter,
sender.date AS date,
mediaseller.contract_number AS contract,
sender.company AS company,
brand.value AS brand,
sender.message_category AS message_category,
sender.message_format AS message_format,
sender.senderid AS senderid,
cpname.value AS cpname,
sid.value AS sid,
status.status AS status,
sender.remarks AS remarks,
user.name AS name,
sender.id AS download,
mediaseller.value AS mediaseller,
lob.value AS lob,
lob.subvalue AS sublob,
sms_type.value AS type_sms,
status.approval_date,
status.batch_date,
status.done_date,
status.decline_date
FROM status
LEFT JOIN sender ON status.trxid = sender.trxid
LEFT JOIN user ON status.userid = user.id
LEFT JOIN mediaseller ON sender.mediaseller = mediaseller.id
LEFT JOIN lob ON sender.industry_category = lob.id
LEFT JOIN sms_type ON sender.type_sms = sms_type.id
LEFT JOIN cpname ON sender.cpname = cpname.id
LEFT JOIN sid ON sender.trxid = sid.trxid
LEFT JOIN brand ON sender.brand = brand.id
WHERE status.hidden = 0
ORDER BY status.id DESC LIMIT 10
I hopeful is query takes one seconds :D
Please give me advice, Thankyou!
You are not filtering by anything other than the status. So try this:
FROM (SELECT s.*
FROM status s
WHERE s.hidden = 0
ORDER BY status.id DESC
LIMIT 10
) status
LEFT JOIN sender ON status.trxid = sender.trxid
LEFT JOIN user ON status.userid = user.id
LEFT JOIN mediaseller ON sender.mediaseller = mediaseller.id
LEFT JOIN lob ON sender.industry_category = lob.id
LEFT JOIN sms_type ON sender.type_sms = sms_type.id
LEFT JOIN cpname ON sender.cpname = cpname.id
LEFT JOIN sid ON sender.trxid = sid.trxid
LEFT JOIN brand ON sender.brand = brand.id
WHERE status.hidden = 0
ORDER BY status.id DESC LIMIT 10
You still need the outer ORDER BY and LIMIT, but they should be on much less data resulting in a performance improvement.
Note: I assume that you have declared all the ids as primary keys, so they have indexes.
I want to return the personal best for a user, by class & round; but the Date Shot is coming out incorrect. Help please - so frustrating!
SELECT
c.Class,
r.Round,
h.shootdate as 'Date Shot',
max(h.Score) AS 'Personal Best'
FROM history h, classes c, rounds r
WHERE c.id = h.classid AND r.id = h.roundid AND h.userid = 1
GROUP BY c.Class, r.Round
You could use a self join on history table to pick a row with maximum score for each user per classid and roundid
SELECT
c.Class,
r.Round,
h.shootdate as 'Date Shot',
h.Score AS 'Personal Best'
FROM history h
JOIN (
SELECT classid, roundid, max(score) score
FROM history
WHERE userid = 1
GROUP BY classid, roundid
) h1 ON h.classid = h1.classid AND h.roundid = h1.roundid AND h.score = h1.score
JOIN classes c ON c.id = h.classid
JOIN rounds r ON r.id = h.roundid
-- WHERE h.userid = 1 // not necessary
In your query you are picking shootdate which is not present in group by that is why you are not getting correct value where score is max, Also use explicit join syntax to relate your tables
I have a database schema like this picture:
I want to write a query that select data of all 6 tables and a field that indicate whether a specific user applied for a job or not.
I've tried:
SELECT j.id, j.expired_date, j.title, j.views, j.status
, j.unread, j.applicants, j.location, j.created_date
, j.contract_type, c.country
, group_concat(DISTINCT jp.page_name) AS fan_pages
, group_concat(DISTINCT jp.id_page) AS id_pages
, app.id AS applied
FROM jobs AS j
INNER JOIN country AS c ON c.id = j.country
LEFT JOIN job_pages AS jp ON jp.id_job = j.id
LEFT JOIN applications AS app ON app.id_job = j.id
LEFT JOIN resumes AS res ON res.id = app.id_resume
LEFT JOIN applicants AS alc ON alc.id = res.id_applicant
AND alc.id_user = 15
WHERE ( j.status = 0 )
AND ( j.expired_date = 0
OR j.expired_date > 1323228856 )
GROUP BY `j`.`id`
ORDER BY `j`.`id` desc
LIMIT 5 OFFSET 5
But it return a result that indicates a job was applied by any user. How can I rewrite this query?
-- Edit --
Below is a basic ERD for how it would be easier to track users who have applied to jobs. I made the relationship between User and Resume a 1:M, in case you wanted to track resume versions. If not, it should be a 1:1 relationship.
So given the ERD, you have a user apply to a job with a resume. If you want to make the resume optional, then you remove the Resume table from the M:M with Job and link directly to User.
Just some ideas...
-- Original --
Just some advice.
Seems to me that you may need to re-visit the schema design. It seems like the applicants table should be a pivot table between the users and jobs tables. The users and jobs table have a M:M relationship in that many users can apply to many jobs and many jobs can be applied to by many users. The applicants table should act as a transactional table when a user applies to a job.
Also, shouldn't the resumes table be directly linked to the users table? How can an application own a resume?
User owns a resume.
User applies to a job with a resume (applicant).
Try it,
SELECT j.id_user as creator, alc.id_user as applier, j.id , j.expired_date, j.title, j.views, j.status
, j.unread, j.applicants, j.location, j.created_date
, j.contract_type, c.country
, group_concat(DISTINCT jp.page_name) AS fan_pages
, group_concat(DISTINCT jp.id_page) AS id_pages
, MAX(app.id) AS applied
FROM jobs AS j
INNER JOIN country AS c ON c.id = j.country
LEFT JOIN job_pages AS jp ON jp.id_job = j.id
LEFT JOIN applications AS app ON app.id_job = j.id
LEFT JOIN resumes AS res ON res.id = app.id_resume
LEFT JOIN applicants AS alc ON alc.id = res.id_applicant
WHERE
( alc.id_user = 15 or alc.id_user IS NULL) AND
( j.status = 0 )
AND ( j.expired_date = 0
OR j.expired_date > 1323228856 )
GROUP BY `j`.`id`
ORDER BY `j`.`id` desc
UPDATE
I believe that, now the query is better:
SELECT
j.id, j.expired_date, j.title, j.views, j.status
, j.unread, j.applicants, j.location, j.created_date
, j.contract_type, c.country
, group_concat(DISTINCT jp.page_name) AS fan_pages
, group_concat(DISTINCT jp.id_page) AS id_pages
, max(app.id) AS applied
FROM users AS u
LEFT JOIN jobs AS j ON 1
INNER JOIN country AS c ON c.id = j.country
LEFT JOIN job_pages AS jp ON jp.id_job = j.id
LEFT JOIN applicants AS alc ON alc.id_user = u.id
LEFT JOIN resumes AS res ON res.id_applicant = alc.id
LEFT JOIN applications AS app ON app.id_resume = res.id AND app.id_job = j.id
WHERE u.id = 16 AND
( j.status = 0 )
AND ( j.expired_date = 0 OR j.expired_date > 1323228856 )
GROUP BY j.id
ORDER BY j.id
New updates:
Use MAX function if you want to get app.id because when you group one or more rows the max function will return correctly the id you want, else only first row will be return and it could be wrong with NULL
Join with the tables users and jobs
And join with applications should be with id_resume and id_job
I'm trying to combine the results of two queries. I'm not very proficient in mysql so I'm here for some help.
The first query is as follows:
select count(roomtypeid) as bookedrooms, day
from event_guest_hotel
where hotelid = 1 and roomtypeid = 1
group by day;
This returns:
The second query:
SELECT ehr.reservationid, ehr.day, h.name AS hotelname,
ehr.totalrooms as requested_rooms, r.name AS roomname
FROM event_hotel_reservation ehr
INNER JOIN hotel_room_type r
ON ehr.roomtypeid = r.roomtypeid
INNER JOIN hotel h
ON ehr.hotelid = h.hotelid
WHERE totalRooms != 0
AND reservationID = '1'
This returns:
Can I combine the first query with the second one, so I get the results of the first one in another resultcolumn next to 'roomname'? That way I know how many rooms are already booked and how many were originally requested from one single query.
Try:
SELECT ehr.reservationid, ehr.day, h.name AS hotelname,
ehr.totalrooms as requested_rooms, r.name AS roomname,
egh.bookedrooms
FROM event_hotel_reservation ehr
INNER JOIN hotel_room_type r ON ehr.roomtypeid = r.roomtypeid
INNER JOIN hotel h ON ehr.hotelid = h.hotelid
left outer join (
select hotelid, count(roomtypeid) as bookedrooms, day
from event_guest_hotel
where roomtypeid = 1
group by hotelid, day
) egh on h.hotelid = egh.hotelid and ehr.day = egh.day
WHERE totalRooms != 0
AND reservationID = '1'
The query below sums up points from the MySQL table "comment" when the following conditions are met:
The loginid when l.username = '$profile' is found.
All the submissionids are found that have the loginid from #1 above.
All the commentids with the submissionids from #2 above are found, and the corresponding points are summed.
Now, how could I make a different query that returns an array of all of the comments in #3 above rather than summing the points?
Here are the MySQL tables involved:
login:
logind username created activated
submission:
submissionid loginid
comment:
commentid submissionid points comment
Query:
SELECT
l.loginid,
l.username,
l.created,
l.activated,
COALESCE(scs.total, 0) AS commentsreceivedvalue
FROM login l
LEFT JOIN (
SELECT S2.loginid, SUM(C2.points) AS total
FROM submission S2
INNER JOIN comment C2
ON S2.submissionid = C2.submissionid
GROUP BY S2.loginid
) scs ON scs.loginid = l.loginid
WHERE l.activated = 1
AND l.username = '$profile'
GROUP BY l.loginid
ORDER BY commentsreceivedvalue DESC
Isn't it as simple as:
SELECT l.loginid, l.username, l.created, l.activated, scs.commentid, scs.comment
FROM login AS l
LEFT JOIN (SELECT S2.loginid, c2.commentid, c2.comment
FROM submission AS S2
JOIN comment AS C2 ON S2.submissionid = C2.submissionid
) AS scs ON scs.loginid = l.loginid
WHERE l.activated = 1
AND l.username = '$profile'
ORDER BY scs.commentid DESC;
The outer GROUP BY in the original was not doing anything useful. The ORDER BY in the original was replaced here by the ordering in reverse order of comment ID, which is an approximation to reverse chronological order (most recent first, in other words).