Been banging my head against the wall and cannot solve this :\
SELECT
`people`.*,
`students`.*,
`student_class_relationships`.*,
`geo_checkin_on_campus`.`datetime_created` as checkin_time
FROM `student_class_relationships`
LEFT OUTER JOIN `students`
ON `student_class_relationships`.`student` = `students`.`id`
LEFT OUTER JOIN `people`
ON `students`.`student` = `people`.`id`
LEFT OUTER JOIN `geo_checkin_on_campus`
ON `students`.`id` = (
SELECT MIN(`geo_checkin_on_campus`.`student`)
FROM `geo_checkin_on_campus`
WHERE `geo_checkin_on_campus`.`student` = `students`.`id`
)
WHERE `class` = 56
The expected result is many rows that have only one entry per students.id.
Here is my schema
It is not the best query from performance perspective,
but just to fix your query here is my attempt:
SELECT
`people`.*,
`students`.*,
`student_class_relationships`.*,
geoCheckinOnCampus.datetimeCreated as checkin_time
FROM `student_class_relationships`
LEFT JOIN `students`
ON `student_class_relationships`.`student` = `students`.`id`
LEFT JOIN `people`
ON `students`.`student` = `people`.`id`
LEFT JOIN
(
SELECT
student,
MAX(datetime_created) datetimeCreated
FROM `geo_checkin_on_campus`
GROUP BY `student`
) geoCheckinOnCampus
ON `students`.`id` = geoCheckinOnCampus.`student`
WHERE `class` = 56
Note According to #xQbert answer I would really change MIN to MAX function if you are looking for the latest datetime.
If i assume you want the most recent checkin (and not the earliest created date) for each student in go_checkin_on_Campus then...
SELECT
`people`.*,
`students`.*,
`student_class_relationships`.*,
B.`datetime_Updated` as checkin_time
FROM `student_class_relationships`
LEFT OUTER JOIN `students`
ON `student_class_relationships`.`student` = `students`.`id`
LEFT OUTER JOIN `people`
ON `students`.`student` = `people`.`id`
LEFT OUTER JOIN (
SELECT max(datetime_updated), student
FROM `geo_checkin_on_campus`
group by student
) B
ON `students`.`id` = B.Student
WHERE `class` = 56
NOTE: This is a probable answer. I will edit / modify this according to comments from OP.
This basically does nothing. This is as good as just joining on the student id
LEFT OUTER JOIN `geo_checkin_on_campus`
ON `students`.`id` = (
SELECT MIN(`geo_checkin_on_campus`.`student`)
FROM `geo_checkin_on_campus`
WHERE `geo_checkin_on_campus`.`student` = `students`.`id`
)
If you want the min (or earliest) datetime_created use something like
LEFT OUTER JOIN (
SELECT `geo_checkin_on_campus`.`student` student,
MIN(`geo_checkin_on_campus`.`datetime_created`) dt
FROM `geo_checkin_on_campus`
WHERE `geo_checkin_on_campus`.`student` = `students`.`id`
GROUP BY `geo_checkin_on_campus`.`student`
) t
ON `students`.`id` = t.student
Related
I am trying to select the latest row from a LEFT JOIN not on the main query. This is my SQL:
SELECT *
FROM messages
LEFT JOIN users
ON messages.message_to = users.user_id
OR messages.message_user = users.user_id
LEFT JOIN message_replies
ON messages.message_id = message_replies.message_reply_main
LEFT JOIN user_personal_information
ON users.user_id =
user_personal_information.user_personal_information_user
I want to select the latest row from:
LEFT JOIN message_replies
ON messages.message_id = message_replies.message_reply_main
My column is called: message_reply_date - how can I use that to LEFT JOIN the latest row?
message_replies:
CREATE TABLE IF NOT EXISTS `message_replies` (
`message_reply_id` int(11) NOT NULL,
`message_reply_user` int(11) NOT NULL,
`message_reply_main` int(11) NOT NULL,
`message_reply_message` text NOT NULL,
`message_reply_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`message_reply_read` int(11) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
I am using this for the WHERE clause:
WHERE m.message_user = ?
OR m.message_to = ?
AND m.message_deleted=0
AND m.message_permdeleted=0
GROUP BY mr.message_reply_main
ORDER BY mr.message_reply_date DESC
If i understood the question, i'll do it this simple way:
SELECT *
FROM messages
LEFT JOIN users
ON messages.message_to = users.user_id OR messages.message_user = users.user_id
LEFT JOIN message_replies
ON messages.message_id = message_replies.message_reply_main
LEFT JOIN user_personal_information
ON users.user_id = user_personal_information.user_personal_information_user
WHERE message_replies.message_reply_date = (SELECT MAX(message_reply_date) FROM message_replies WHERE message_reply_main = messages.message_id)
/* AND more criterias */
No grouping in the main query but just using a MAX evaluation of message_reply_date in a subquery regarding the WHERE criteria on message_reply_date itself.
There is a "left join" method for getting the most recent message (as well as several others). But in keeping with the preferred join method of the question:
SELECT *
FROM messages m LEFT JOIN
users u
ON m.message_to = u.user_id OR m.message_user = u.user_id LEFT JOIN
message_replies mr
ON m.message_id = mr.message_reply_main LEFT JOIN
user_personal_information upi
ON u.user_id = upi.user_personal_information_user LEFT JOIN
message_replies mr2
ON mr2.message_reply_main = m.message_id AND
mr2.message_reply_date > mr.message_reply_date
WHERE mr2.message_reply_main IS NULL;
I also added table aliases because they make a query easier to write and to read.
The idea is to match to the table again, but only for later messages. Then, the WHERE clause checks that none exist -- so it has the latest one.
Give this a go.
SELECT *
FROM messages
LEFT JOIN users
ON messages.message_to = users.user_id
OR messages.message_user = users.user_id
LEFT JOIN ( SELECT *
, MAX(message_reply_date) OVER (PARTITION BY message_id) AS most_recent_message_reply_date
FROM message_replies ) message_replies
ON messages.message_id = message_replies.message_reply_main
AND message_replies.message_reply_date = message_replies.most_recent_message_reply_date
LEFT JOIN user_personal_information
ON users.user_id =
user_personal_information.user_personal_information_user
I've substituted your direct call to the message_replies table with a Select that displays the table and the maximum reply date grouped by the message_id. I've then included this in the join to filter all answers other than the one your looking for.
Have a go, any problem drop me a message, always happy to help.
What you want to do is emulate the rank & partition functionality seen in MSSQL & PL/SQL, essentially ranking on date & partitioning by message_reply_main.
This can be done in MySQL using an outer join & a count on your partition, while using a < or > in the join for the ranking criteria. This allows you to do your standard join, but also where rank = 1, returning the largest date.
SELECT *
FROM messages
LEFT JOIN users
ON messages.message_to = users.user_id
OR messages.message_user = users.user_id
LEFT JOIN (select mri.message_reply_main
, mri.message_reply_date
, mri.message_reply_message
, mri.message_reply_read
, count(mri2.message_reply_main) + 1 as rank
FROM message_replies mri
left join message_replies mri2 on mri.message_reply_date < mri2.message_reply_date
and mri.message_reply_main = mri2.message_reply_main
group by mri.message_reply_main, mri.message_reply_message
, mri.message_reply_read, mri.message_reply_date) mr ON messages.message_id = mr.message_reply_man
AND mr.rank = 1
LEFT JOIN user_personal_information
ON users.user_id =
user_personal_information.user_personal_information_user
SELECT UserDCID AS UserDCCompForDelCount1
FROM CDICentral.dbo.UserDCComponent AS udc
WHERE(
UserDCID IN(
SELECT UserDCID FROM CDICentral.dbo.UserDC AS ud WHERE (
UserID NOT IN(
SELECT UserID FROM CVPLocal.dbo.CSWSecurity AS cs
)
)
)
)
Try this sql query
SELECT UserDCID AS UserDCCompForDelCount1
FROM CDICentral.dbo.UserDCComponent AS udc
INNER JOIN CDICentral.dbo.UserDC AS ud ON udc.UserDCID=ud.UserDCID
INNER JOIN CVPLocal.dbo.CSWSecurity AS cs ON ud.UserDCID=cs.UserID
Here is the query
SELECT UserDCID AS UserDCCompForDelCount1 FROM CDICentral.dbo.UserDCComponent AS udc
INNER JOIN (
SELECT UserDCID FROM CDICentral.dbo.UserDC AS ud
EXCEPT
SELECT UserID FROM CVPLocal.dbo.CSWSecurity AS cs
)Table T
ON T.UserDCID = udc.UserDCID
Let's start with the inner queries:
SELECT UserDCID FROM CDICentral.dbo.UserDC AS ud WHERE (
UserID NOT IN(
SELECT UserID FROM CVPLocal.dbo.CSWSecurity AS cs
)
)
They select the rows from table ud that don't have a match in table cs, joining the tables using the field UserID which is present in both of them.
The query can be written as a LEFT JOIN:
SELECT ud.UserDCID
FROM CDICentral.dbo.UserDC AS ud
LEFT JOIN CVPLocal.dbo.CSWSecurity AS cs
ON ud.UserID = cs.UserID # join the tables on common field `UserID`
WHERE cs.UserID IS NULL # the row from `ud` has no match in `cs`
Back to the big query (I stripped the part that was refactored above because stays in the way for the explanation below):
SELECT UserDCID AS UserDCCompForDelCount1
FROM CDICentral.dbo.UserDCComponent AS udc
WHERE(
UserDCID IN (
SELECT UserDCID ...
)
)
)
This query selects the rows from udc that have matches in the result set produced by the inner query (the stripped one) when udc and the result set are joined by field UserDCID which is present in both.
This is a simple INNER JOIN (let's put the refactored inner query back):
SELECT udc.UserDCID AS UserDCCompForDelCount1
FROM CDICentral.dbo.UserDCComponent AS udc
INNER JOIN CDICentral.dbo.UserDC AS ud
ON udc.UserDCID = ud.UserDCID
LEFT JOIN CVPLocal.dbo.CSWSecurity AS cs
ON ud.UserID = cs.UserID
WHERE cs.UserID IS NULL
Let's read what the new query does: it selects UserDCComponents (udc) associated with UserDCs (ud) that don't have CSWSecurity (cs) records.
I have the following influences table:
I need to get the latest influence for a stakeholder...
I have the following query but it's mixing up the row data because MAX isn't returning a full record:
SELECT stakeholder_id, MAX(created_at) AS maxca, influence
FROM influences
WHERE
project_id = 1 AND
deleted_at IS NULL
GROUP BY stakeholder_id
You can see that the influence against that maxca and stakeholder_id should be 3, not 5.
How do I overcome this issue?
My full existing statement was this:
SELECT `stakeholders`.*, `influences`.`influence`
FROM `project_stakeholder`
INNER JOIN `stakeholders` ON `project_stakeholder`.`stakeholder_id` = `stakeholders`.`id`
INNER JOIN `stakeholder_profiles` ON `stakeholder_profiles`.`stakeholder_id` = `stakeholders`.`id`
LEFT JOIN `stakeholder_profile_tag` ON `stakeholder_profile_tag`.`stakeholder_profile_id` = `stakeholder_profiles`.`id`
LEFT JOIN `stakeholder_profile_group` ON `stakeholder_profile_group`.`stakeholder_profile_id` = `stakeholder_profiles`.`id`
LEFT JOIN `influences` ON `influences`.`stakeholder_id` = `stakeholders`.`id`
INNER JOIN `projects` ON `project_stakeholder`.`project_id` = `projects`.`id`
LEFT JOIN (
/*! This is the bit that doesn't work */
SELECT stakeholder_id, MAX(created_at) AS maxca
FROM influences
WHERE
project_id = 1 AND
deleted_at IS NULL
GROUP BY stakeholder_id
)
iu ON `iu`.`stakeholder_id` = influences.stakeholder_id AND
iu.maxca = influences.created_at
WHERE `projects`.`id` = '1'
GROUP BY `stakeholders`.`id`
This seems to work:
SELECT `stakeholders`.*, iu.influence
FROM `project_stakeholder`
INNER JOIN `stakeholders` ON `project_stakeholder`.`stakeholder_id` = `stakeholders`.`id`
INNER JOIN `stakeholder_profiles` ON `stakeholder_profiles`.`stakeholder_id` = `stakeholders`.`id`
LEFT JOIN `stakeholder_profile_tag` ON `stakeholder_profile_tag`.`stakeholder_profile_id` = `stakeholder_profiles`.`id`
LEFT JOIN `stakeholder_profile_group` ON `stakeholder_profile_group`.`stakeholder_profile_id` = `stakeholder_profiles`.`id`
INNER JOIN `projects` ON `project_stakeholder`.`project_id` = `projects`.`id`
LEFT JOIN (
select i1.*
from influences i1
join
(
SELECT stakeholder_id, MAX(created_at) AS maxca
FROM influences
WHERE project_id = 1
AND deleted_at IS NULL
GROUP BY stakeholder_id
) i2 on i1.stakeholder_id = i2.stakeholder_id
and i1.created_at = i2.maxca
) iu ON `iu`.`stakeholder_id` = stakeholders.id
WHERE `projects`.`id` = '1'
GROUP BY `stakeholders`.`id`
select i1.*
from influences i1
join
(
SELECT stakeholder_id, MAX(created_at) AS maxca
FROM influences
WHERE project_id = 1
AND deleted_at IS NULL
GROUP BY stakeholder_id
) i2 on i1.stakeholder_id = i2.stakeholder_id
and i1.created_at = i2.maxca
Here's a fiddle with the schema and a sample query: http://sqlfiddle.com/#!2/573ec4/9
I'm looking to return C.price using I.section and L.level, which are obtained via other joins. When using an inner join with Cost I am left with no result: http://sqlfiddle.com/#!2/573ec4/2
Since cost is a table that maps a section and level to a price, I would like to be able to calculate the price of all the showings in my query
When you are joining to the Cost table you do not include the proper join conditions:
INNER JOIN `cost` `C`
ON `I`.`Section` = c.section -- = c.section is missing
AND `L`.`level` = c.level; -- = c.level is missing
So your full query will be:
SELECT `F`.`date`,
`F`.`time`,
`F`.`tname`,
`I`.`section`,
`L`.`level`,
c.price
FROM `booking_for_schedule` `F`
INNER JOIN `booking_in_seats` `I`
on `F`.`tname`=`I`.`tname`
AND `F`.`booking_num` = `I`.`booking_num`
INNER JOIN `level` `L`
on `F`.`date`=`L`.`date`
AND `F`.`time`=`L`.`time`
AND `F`.`tname`=`L`.`tname`
INNER JOIN `cost` `C`
ON `I`.`Section` = c.section
AND `L`.`level` = c.level;
See SQL Fiddle with Demo
Looks like you're missing some clause on the INNER JOIN cost C (when you are joining on the Cost table).
It should look like this:
INNER JOIN `cost` `C` ON `I`.`Section`=`C`.`Section` AND `L`.`level`=`C`.`level`
instead of:
INNER JOIN `cost` `C` ON `I`.`Section` AND `L`.`level`
You have missed to join the columns. the reason why you have no result is because I.Section AND L.level will always return false.
SELECT F.date,
F.time,
F.tname,
I.section,
L.level,
c.*
FROM booking_for_schedule F
INNER JOIN booking_in_seats I
ON F.tname = I.tname AND
F.booking_num = I.booking_num
INNER JOIN level L
ON F.date = L.date AND
F.time = L.time AND
F.tname = L.tname
INNER JOIN cost C
ON I.Section = C.Section AND -- <<== HERE
L.level = C.level -- <<== HERE
SQLFiddle Demo
Your last line is the problem:
INNER JOIN `cost` `C` ON `I`.`Section` AND `L`.`level`
It should be this:
INNER JOIN `cost` `C` ON `I`.`Section` = `C`.`Section` AND `L`.`level` = `C`.`Level`
I think I should know this somehow, especially after reading a lot of questions and answers regarding "The condition must go into the ON clause, not in the WHERE clause". However, I am still lost.
I have three tables, and I join them normally with LEFT (OUTER) joins. The joined tables looks like this (retty standard):
task_id task_questions_taskId taskQuestions_questionId question_id
1 1 5 5
1 1 8 8
2 2 8 8
SELECT `t`.`id` AS `task_id` ,
`task_questions`.`taskId` AS `task_questions_taskId` ,
`task_questions`.`questionId` AS `task_questions_questionId` ,
questions.id AS question_id
FROM `task` `t`
LEFT OUTER JOIN `task_questions` `task_questions`
ON ( `task_questions`.`taskId` = `t`.`id` )
LEFT OUTER JOIN `question` `questions`
ON ( `task_questions`.`questionId` = `questions`.`id` )
This is the standard query to get all the records. (It's taken from Yii; I actually want to to this with Active Record, but can't even get plain SQL right).
And now I want to get ONLY those tasks that have the question_id 2 AND 8 (e.g)
So if a task has not both of those question.ids, I don't want it in the result set.
In this case, the task could have other question_ids, too. Although it would be interesting to see how the query would look if it should return only those that have exactly those 2 (or any other set).
It's easy to get all the tasks that have one question, with WHERE question.id = 2,
but an AND in the WHERE clause leads to an empty result.
The WHERE clause can only apply conditions to one row at a time. But your questions of different id occur on different rows. How to solve this? Join both rows onto one row using a self-join.
Here's an example:
SELECT t.`id` AS `task_id`, ...
FROM `task` AS t
INNER JOIN `task_questions` AS tq2 ON ( tq2.`taskId` = t.`id` )
INNER JOIN `questions` AS q2 ON ( tq2.`questionId` = q2.`id` )
INNER JOIN `task_questions` AS tq8 ON ( tq8.`taskId` = t.`id` )
INNER JOIN `questions` AS q8 ON ( tq8.`questionId` = q8.`id` )
WHERE q2.`id` = 2 AND q8.`id` = 8
Another solution is to find the tasks that have questions 2 OR 8, and then use GROUP BY and HAVING to filter by groups that have exactly two of those.
SELECT t.`id` AS `task_id`, ...
FROM `task` AS t
INNER JOIN `task_questions` AS tq ON ( tq.`taskId` = t.`id` )
INNER JOIN `questions` AS q ON ( tq.`questionId` = q.`id` )
WHERE tq.`questionId` IN (2, 8)
GROUP BY t.`id`
HAVING COUNT(DISTINCT q.`id`) = 2
you can do this even with out using and
... where question.id IN (2,8)
Use IN:
SELECT `t`.`id` AS `task_id` ,
`task_questions`.`taskId` AS `task_questions_taskId` ,
`task_questions`.`questionId` AS `task_questions_questionId` ,
questions.id AS question_id
FROM `task` `t`
LEFT OUTER JOIN `task_questions` `task_questions`
ON ( `task_questions`.`taskId` = `t`.`id`)
LEFT OUTER JOIN `question` `questions`
ON ( `task_questions`.`questionId` = `questions`.`id` )
WHERE `task_questions`.`questionId` IN (2, 8)
This should do it
SELECT `t`.`id` AS `task_id` ,
`task_questions`.`taskId` AS `task_questions_taskId` ,
`task_questions`.`questionId` AS `task_questions_questionId` ,
questions.id AS question_id
FROM `task` `t`
LEFT OUTER JOIN `task_questions` `task_questions`
ON ( `task_questions`.`taskId` = `t`.`id` )
LEFT OUTER JOIN `question` `questions`
ON ( `task_questions`.`questionId` = `questions`.`id` )
WHERE questions.id in (2,8)
You're not looking for AND, you're looking for OR, or an IN:
WHERE `questions`.`id` IN (2,8) -- grab everything in the parens.
Or
WHERE `questions`.`id` = 2 OR -- grab each item individually
`questions`.`id` = 8
If you use AND that would mean the ID would have to be 8 and 2 at the same time. Bad deal.