Subquery Slows Query down significantly - mysql

I'm trying to do a query that replaces multiple queries to produce a result set. I've got a fairly straight forward set of 1 to 1 joins, but one column I'm trying to get from a table that keeps multiple records per primary record. Basically it's like this:
meet_entries with a primary key id column and a bunch of other columns,
meet_entries_statuses, with an autoincrement id column and a foreign key to the id column of meet_entry
There may be multiple rows in meet_entry_statuses for each row in meet_entries. In my query, I'm trying to get the most recently added(so highest id number) row associated with the meet_entry primary key id.
Here's my query:
SELECT
meet_entries.id,
member.firstname,
member.surname,
member.gender,
member.dob,
clubs.code,
clubs.clubname,
meet_entries.meals,
meet_entries.massages,
meet_entries.cost,
meet_entries.cancelled,
(SELECT meet_entry_status_codes.description
FROM meet_entry_statuses, meet_entry_status_codes
WHERE meet_entry_statuses.code = meet_entry_status_codes.id
AND meet_entry_statuses.id = meet_entries.id
ORDER BY meet_entry_statuses.id DESC
LIMIT 1) as status,
COUNT(DISTINCT meet_events_entries.id) as entries
FROM meet_entries, meet_events_entries, member, clubs, meet_entry_statuses, meet_entry_status_codes
WHERE meet_entries.meet_id = ?
AND meet_entries.id = meet_events_entries.meet_entry_id
AND meet_entries.member_id = member.id
AND meet_entries.club_id = clubs.id
GROUP BY meet_entries.id
;
Adding the bit:
(SELECT meet_entry_status_codes.description
FROM meet_entry_statuses, meet_entry_status_codes
WHERE meet_entry_statuses.code = meet_entry_status_codes.id
AND meet_entry_statuses.id = meet_entries.id
ORDER BY meet_entry_statuses.id DESC
LIMIT 1) as status,
takes the query from being .2 of a second, to never finishing. There are only 10 matching rows in meet_entries. There are about 50 rows in the meet_entries_statuses appropriate to the 10 rows in meet_entries, but as I say I'm trying to only get 1 row for each meet_entries row. Any suggestions on a better way to do this?

I am not very sure why you would join with meet_entry_statuses and meet_entry_status_codes without any join conditions. Doing so means that you are doing a cross join. So if there are N rows in meet_entries, and M and P rows in the above two tables respectively, you end up generating N x M x P rows.
Can you try taking out the two joins. I marked them out in the following rewritten query:
SELECT
meet_entries.id,
member.firstname,
member.surname,
member.gender,
member.dob,
clubs.code,
clubs.clubname,
meet_entries.meals,
meet_entries.massages,
meet_entries.cost,
meet_entries.cancelled,
(SELECT
meet_entry_status_codes.description
FROM
meet_entry_statuses as mes
, meet_entry_status_codes as mesc
WHERE
meet_entry_statuses.code = meet_entry_status_codes.id
AND meet_entry_statuses.id = meet_entries.id
ORDER BY meet_entry_statuses.id DESC
LIMIT 1
) as status,
COUNT(DISTINCT meet_events_entries.id) as entries
FROM
meet_entries as me
, meet_events_entries as mee
, member as m
, clubs as c
, meet_entry_statuses as mes -- try taking this out
, meet_entry_status_codes as mesc -- try taking this out
WHERE
meet_entries.meet_id = ?
AND meet_entries.id = meet_events_entries.meet_entry_id
AND meet_entries.member_id = member.id
AND meet_entries.club_id = clubs.id
GROUP BY meet_entries.id
;

Related

Disacrding multiple duplicate records from a left join

I have two tables:-
gallery
gallery_favorite
The user_id in gallery table means the user who posted the item. The user_id in gallery_favorite means the user who added the item in his favorite list. If favorite = 0, then it means the user had initially added the item in favorite list but later removed it.
Now, I want to fetch all the gallery items along with its favorite status. Here is my query:-
Select distinct `gallery`.`id`, `gallery`.`caption`, `gallery`.`type`,
`gallery`.`video`, `gallery`.`image`, `gallery`.`type`,
`gallery`.`created_date`, `gallery`.`modified_date`,
`gallery_favorite`.`favorite`, `gallery`.`user_id`
from `gallery`
left join `gallery_favorite` on `gallery_favorite`.`gallery_id` = `gallery`.`id`
where
(`gallery`.`type` = 'i'
and `gallery`.`status` = 1
and `gallery`.`deleted` = 0)
and
((`gallery`.`user_id` != 11 and `gallery`.`private` = 0)
or `gallery`.`user_id` = 11)
limit 20 offset 0
But a syou can see, I am getting duplicate records depending upon the number of rows wrt to a gallery item in the gallery favorite table. How can I modify the query to get only one record (along with my own favorite status)?
I guess you are getting duplicate records because you have not joined both the table on user_id -
Try below query -
Select distinct `gallery`.`id`, `gallery`.`caption`, `gallery`.`type`,
`gallery`.`video`, `gallery`.`image`, `gallery`.`type`,
`gallery`.`created_date`, `gallery`.`modified_date`,
`gallery_favorite`.`favorite`, `gallery`.`user_id`
from `gallery`
left join `gallery_favorite` on `gallery_favorite`.`gallery_id` = `gallery`.`id`
and `gallery_favorite`.`user_id` = `gallery`.`user_id`
where
(`gallery`.`type` = 'i'
and `gallery`.`status` = 1
and `gallery`.`deleted` = 0)
and
((`gallery`.`user_id` != 11 and `gallery`.`private` = 0)
or `gallery`.`user_id` = 11)
limit 20 offset 0
Assuming the gallery as many favorites from different users. It makes no sense to display exclusively a hit favorite to a user and gallery alone. Counting them makes sense.
Select `gallery`.`id`, `gallery`.`caption`, `gallery`.`type`,
`gallery`.`video`, `gallery`.`image`, `gallery`.`type`,
`gallery`.`created_date`, `gallery`.`modified_date`,
sum(gallery_favorite`.`favorite`) as total_favorites -- count them group function aggregate
from `gallery`
left join `gallery_favorite` on `gallery_favorite`.`gallery_id` = `gallery`.`id`
where
(`gallery`.`type` = 'i'
and `gallery`.`status` = 1
and `gallery`.`deleted` = 0)
and
((`gallery`.`user_id` != 11 and `gallery`.`private` = 0)
or `gallery`.`user_id` = 11)
and gallery_favorite`.`favorite` = 1 -- count only the favorites
GROUP BY `gallery`.`id` -- GROUP CLAUSE
This is how a join works. You get all rows matching the condition. You can either group the result by the fields in the left table (thus eliminating duplicates in the output) or join with a table that has one entry per gallery item -- this requires joining with a (SELECT ... FROM gallery_favorite GROUP BY gallery_id)

DELETE row FROM table WHERE field value are results of another complex query

I'm trying deleting rows from a table bananas, where id has to be equal to results of this query:
SELECT DISTINCT (ub.userId) as id
FROM new_users_bonus ub
JOIN new_users_used_bonus uub ON ub.userBonusId = uub.userBonusId
JOIN new_bonus_details bd ON bd.bonusId = ub.bonusId
JOIN new_bonus b ON b.bonusId = bd.bonusId
WHERE b.prefix LIKE 'AEJ'
AND (ub.expirationDate <= '2017-10-23'
OR ub.bonusValue - (SELECT SUM(usedValue) as totalUsedValue FROM new_users_used_bonus uub2 where ub.userBonusId = uub2.userBonusId) <= 0)
GROUP BY ub.bonusId, ub.redeemedDate
ORDER BY ub.expirationDate ASC
I've written:
DELETE FROM bananas WHERE id = ANY (complex query written before)
and
DELETE FROM bananas WHERE id IN (complex query written before)
But, it doesn't erase anything.
Why? What's wrong? I don't receive error message...
Thank you!

MySQL Multiple Join Query with Limit on One Join

I have a MYSQL query I'm working on that pulls data from multiple joins.
select students.studentID, students.firstName, students.lastName, userAccounts.userID, userstudentrelationship.userID, userstudentrelationship.studentID, userAccounts.getTexts, reports.pupID, contacts.pfirstName, contacts.plastName, reports.timestamp
from userstudentrelationship
join userAccounts on (userstudentrelationship.userID = userAccounts.userID)
join students on (userstudentrelationship.studentID = students.studentID)
join reports on (students.studentID = reports.studentID)
join contacts on (reports.pupID = contacts.pupID)
where userstudentrelationship.studentID = "10000005" AND userAccounts.getTexts = 1 ORDER BY reports.timestamp DESC LIMIT 1
I have a unique situation where I would like one of the joins (the reports join) to be limited to the latest result only for that table (order by reports.timestamp desc limit 1 is what I use), while not limiting the result quantities for the overall query.
By running the above query I get the data I would expect, but only one record when it should return several.
My question:
How can I modify this query to ensure that I receive all possible records available, while ensuring that only the latest record from the reports join used? I expect that each record will possibly contain different data from the other joins, but all records returned by this query will share the same report record
Provided I understand the issue; one could add a join to a set of data (aliased Z below) that has the max timestamp for each student; thereby limiting to one report record (most recent) for each student.
SELECT students.studentID
, students.firstName
, students.lastName
, userAccounts.userID
, userstudentrelationship.userID
, userstudentrelationship.studentID
, userAccounts.getTexts
, reports.pupID
, contacts.pfirstName
, contacts.plastName
, reports.timestamp
FROM userstudentrelationship
join userAccounts
on userstudentrelationship.userID = userAccounts.userID
join students
on userstudentrelationship.studentID = students.studentID
join reports
on students.studentID = reports.studentID
join contacts
on reports.pupID = contacts.pupID
join (SELECT max(timestamp) mts, studentID
FROM REPORTS
GROUP BY StudentID) Z
on reports.studentID = Z.studentID
and reports.timestamp = Z.mts
WHERE userstudentrelationship.studentID = "10000005"
AND userAccounts.getTexts = 1
ORDER BY reports.timestamp
for get all the records you should avoid limit 1 at the end of the query
for join anly one row from reports table you could use subquery as
select
students.studentID
, students.firstName
, students.lastName
, userAccounts.userID
, userstudentrelationship.userID
, userstudentrelationship.studentID
, userAccounts.getTexts
, t.pupID
, contacts.pfirstName
, contacts.plastName
, t.timestamp
from userstudentrelationship
join userAccounts on userstudentrelationship.userID = userAccounts.userID
join students on userstudentrelationship.studentID = students.studentID
join (
select * from reports
order by reports.timestamp limit 1
) t on students.studentID = t.studentID
join contacts on reports.pupID = contacts.pupID
where userstudentrelationship.studentID = "10000005"
AND userAccounts.getTexts = 1

MySQL how to show rows that also does not match where clause

I have this query:
SELECT `y78f2_students`.`firstname` , `y78f2_students`.`lastName` , `y78f2_students`.`student_id`,`y78f2_attendance`.`is_present`, `y78f2_attendance`.`note`, `y78f2_attendance`.`thedate`
FROM `y78f2_students`
INNER JOIN `y78f2_enrolls` ON `y78f2_enrolls`.`student_id` = `y78f2_students`.`student_id`
INNER JOIN `y78f2_attendance` ON `y78f2_attendance`.`student_id` = `y78f2_students`.`student_id`
WHERE `y78f2_enrolls`.`term` = 'Term 2 2016'
AND `y78f2_enrolls`.`crn_class` = 'Math1R1'
and `y78f2_attendance`.`thedate` = '2016-01-24'
ORDER BY thedate desc
This query returns only the rows where the date is '2016-01-24'. Currently that is just one row: http://i.imgur.com/jRTBqQ0.png
I need to show all rows where term = 'Term 2 2016' and crn_class` = 'Math1R1' and also where the date is not set as yet. In order words I want to show all students in the class and if the date is not set for these students yet, it will show null.
This is what I would like: http://i.imgur.com/jmsuSF5.png
So in summary I need to show rows where the clause is met and those where the date would be null or not exist yet.
How can I write this query?
Try moving the conditions related to the joined tables from the end of the query, up to the table's respective ON clause for each join. Also, if you would like to return records for which no row yet exists in the y78f2_attendance table, that table should be LEFT OUTER joined, not INNER joined.
SELECT `y78f2_students`.`firstname` , `y78f2_students`.`lastName`,
`y78f2_students`.`student_id`,`y78f2_attendance`.`is_present`,
`y78f2_attendance`.`note`, `y78f2_attendance`.`thedate`
FROM `y78f2_students`
INNER JOIN `y78f2_enrolls` ON
`y78f2_enrolls`.`student_id` = `y78f2_students`.`student_id`
AND `y78f2_enrolls`.`crn_class` = 'Math1R1'
LEFT OUTER JOIN `y78f2_attendance` ON
`y78f2_attendance`.`student_id` = `y78f2_students`.`student_id`
AND (`y78f2_attendance`.`thedate` IS NULL OR `y78f2_attendance`.`thedate` = '2016-01-24')
WHERE `y78f2_enrolls`.`term` = 'Term 2 2016'
ORDER BY thedate desc

Count only unique terms based on inner SELECT JOIN

I'm trying to count only unique terms as a TOTAL count.
This is the original query and it works fine
->select('DISTINCT search_tags.term AS t_name, nbr', FALSE)
->from('search_tags LEFT JOIN (SELECT term AS tk, COUNT(search_tags.term) AS nbr FROM search_tags GROUP BY search_tags.term) AS TR ON search_tags.term = TR.tk ')
->where('search_tags.dt_added >=', '2011-08-01 09:48:54')
->where('search_tags.dt_added <=', '2011-09-02 09:48:54');
// returns: [twitter,12],[facebook,6].....
The thing is that this code runs a datatable (datatable.net) so the datatable removes the select line and change it to:
SELECT COUNT(*) AS numrows
FROM (search_tags LEFT JOIN (SELECT term AS tk, COUNT(search_tags.term) AS nbr FROM search_tags GROUP BY search_tags.term) AS TR ON search_tags.term = TR.tk)
WHERE `search_tags`.`dt_added` >= '2011-08-01 09:48:54'
AND `search_tags`.`dt_added` <= '2011-09-02 09:48:54'
// returns the same [twitter,12],[facebook,6]..... BUT the pagination is broken.
So the datatable can count the rows and use it as a pagination param.
But when it removes the select, it get all the rows as the DISTINCT is not there anymore.
I'm sleep deprived so I'm like stuck on try and error forever. Please help lol :P
Sorted out
->select('DISTINCT st5.term AS t_name, n_ocurrences', FALSE)
->from("search_tags AS st4
RIGHT OUTER JOIN (SELECT DISTINCT st.term, n_ocurrences
FROM search_tags AS st
JOIN
(SELECT term AS n_term, COUNT(term) AS n_ocurrences
FROM search_tags AS st2
GROUP BY st2.term)
AS st3 ON st3.n_term = st.term
WHERE st.dt_added >= '$min'
AND st.dt_added <= '$max') AS st5 ON st5.term = 1");
Had to add some inception selects to retrieve the right amount.
Now I need to count the UNIQUE n_ocurrences under the $min, $max as the n_ocurrences is returning only the overall count.