MySQL Multiple Join Query with Limit on One Join - mysql

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

Related

I want to improve the speed of query statements, but I want to know how

When executing a query statement, the speed is very slow.
SELECT
T1.APPL_SEQ
, T1.COMP_CD
, (SELECT COMP_NM FROM tb_company WHERE COMP_CD = T1.COMP_CD) AS COMP_NM
, T1.GPROD_CD
, (SELECT GPROD_NM FROM tb_gprod WHERE GPROD_CD = T1.GPROD_CD) AS GPROD_NM
, T1.SITE_CD
, (SELECT SITE_NM FROM tb_site WHERE SITE_CD = T1.SITE_CD) AS SITE_NM
, T1.INFLOW_CD
, T1.INFLOW_URL
, T1.STATUS
, T1.REG_DTM
, DECRYPTO(T1.NAME) AS NAME
, DECRYPTO(T1.HP) AS HP
, ifnull(T1.AGE,T1.`115`) AS AGE
, ifnull(T1.GENDER,T1.`116`) AS GENDER
, ifnull(T1.MEMO,T1.`120`) AS MEMO
, ifnull(T1.`105`,T1.`124`) AS TIME
, T1.`125` AS AGE_CHILD
, T2.API_YN
, T2.API_START_DT
, T2.API_END_DT
, T2.API_CD
, T2.DATA_INFLOWCD
, T2.CONFIRM_YN
, T2.SALE_YN
, T2.SALE_PRICE
, T2.BREAKDOWN
, T2.INPUT_DATE
, T3.DIST_YN
, T3.DIST_DT
,(select ifnull((select timestampdiff(DAY, T11.REG_DTM,T1.REG_DTM) AS DIFF2REGTIME from tb_applicant T11 WHERE T11.HP = T1.HP AND T11.GPROD_CD = T1.GPROD_CD AND T11.REG_DTM < T1.REG_DTM order by T11.REG_DTM desc limit 1),-1)) AS HP2_COUNT
FROM
tb_applicant T1
LEFT JOIN mm_applicant T2
ON T1.APPL_SEQ = T2.APPL_SEQ
LEFT JOIN dist_applicant T3
ON T1.APPL_SEQ = T3.APPL_SEQ
LEFT JOIN tb_site T4
ON T4.site_cd = T1.SITE_CD and T4.comp_cd = T1.COMP_CD and T4.gprod_cd = T1.GPROD_CD
WHERE 1=1
AND T1.APPL_SEQ > 147293
AND T4.is_use = 'Y'
$Sql_Search
ORDER BY
$Sql_OrderBy
) U1
, (SELECT #ROWNUM := 0) U2
) V1";
,(select ifnull((...),-1)) AS HP2_COUNT
This is part of why it's so slow.
This query calculates the number of months difference by comparing REG_DTM when the td_applicant table has the same data for HP, GPROD, and COMP.
I don't need to get the date difference, is there any way to improve the query speed?
The main problem are those subselect in the select. As #Akina suggested, you should move them in FROM and make them as join.
They way you have done implies that each subselect is executed for each row returned by the main select.
You have 4 subselect that mean if you have 100 rows you execute 1 (main select) + (4*100) query so 401 instead of 1.
Using join allow the internal optimization engine to choose the best strategy to perform the query, in your way practically no optimization are applied.
I post a short example of how should be your query, didn't refactor the whole query since without database is a bit difficult to do it and I can easily produce a wrong query.
Notice that you select twice on tb_site with different condition, so is up to you to put the correct one.
SELECT T1.APPL_SEQ, T1.COMP_CD, T1.GPROD_CD, T1.SITE_CD
TC.COMP_NM,
TG.GPROD_NM,
TS.SITE_NM,
......
FROM tb_applicant T1
LEFT JOIN mm_applicant T2
JOIN tb_company TC on TC.COMP_CD = T1.COMP_CD
JOIN tb_gprod TG on GPROD_CD = T1.GPROD_CD
JOIN tb_site TS on TS.SITE_CD = T1.SITE_CD ON T1.APPL_SEQ = T2.APPL_SEQ
.......

How to use the SELECT clause twice in a sql statement

I want to have a table where I can view today's balance as well as yesterday's balance as two different column. Is there any way I can select from two different dates?
Below is the SQL statement I have tried however I am not able to see the yesterday balance.
(SELECT food.food_id, food.food_name, food.food_chi_name, food.food_category, food.chinesechar, SUM(inventory.tmr_input+inventory.final_balance), SUM(inventory.balance), SUM(inventory.input), SUM(inventory.reject), SUM(inventory.final_balance), SUM(inventory.tmr_input), SUM(inventory.sale), SUM(inventory.theoritical), SUM(inventory.yest_theoritical), SUM(inventory.3PMsale), SUM(inventory.3PMbalance), SUM(inventory.wholesale) FROM inventory INNER JOIN food ON inventory.food_id=food.food_id WHERE food.outlet = 'T11' AND inventory.date = '04/30/2021' GROUP BY food.food_id ORDER BY food.food_id ASC); (SELECT SUM(inventory.balance) as yesterday_balance FROM inventory INNER JOIN food ON inventory.food_id=food.food_id WHERE food.outlet = 'T11' AND inventory.date = '04/29/2021' GROUP BY food.food_id ORDER BY food.food_id ASC);
You can use a subselect for that purpose
And using aliases helps to get a better overview
SELECT
f1.food_id
, f1.food_name
, f1.food_chi_name
, f1.food_category
, f1.chinesechar
, SUM(i1.tmr_input+i1.final_balance)
, SUM(i1.balance)
, SUM(i1.input)
, SUM(i1.reject)
,SUM(i1.final_balance)
, SUM(i1.tmr_input)
, SUM(i1.sale)
, SUM(i1.theoritical)
, SUM(i1.yest_theoritical)
, SUM(i1.`3PMsale`)
, SUM(i1.`3PMbalance`)
, SUM(i1.wholesale)
,(SELECT SUM(i2.balance)
FROM
inventory i2 INNER JOIN food f2 ON i2.food_id=f2.food_id
WHERE f2.outlet = 'T11' AND i2.date = '04/29/2021'
AND f2.food_id = f1.food_id
) as yesterday_balance
FROM
inventory i1
INNER JOIN
food f1 ON i1.food_id=f1.food_id
WHERE
f1.outlet = 'T11'
AND i1.date = '04/30/2021'
GROUP BY f1.food_id
ORDER BY f1.food_id ASC;

MySQL query joining 3 tables and counting

I've been stuck on this problem for far too long.
I have to merge 3 tables and do some counting of distinct values.
I have 3 tables
1.User_me
profileId( String )
responded( int 1 or 0)
2.Profiles
profileId ( String )
idLocation ( int )
3.lookup_location
id ( int )
location (String )
I can join User_me and Profiles ON User_me.profileId = Profiles.profileId
I can join Profiles and lookup_location ON Profiles.idLocation = lookup_location.id
Under Profiles I need to count the number of distinct values for idLocation where User_me.profileId = Profiles.profileId
I also need to count the number of Profiles.idLocation that have User_me.responded = 1
I have this:
SELECT lookup.location, count(*) as total
FROM User_me user
JOIN Profiles
ON user.profileId= profiles.profileId
JOIN lookup_location lookup
ON profiles.idLocation = lookup.id
GROUP BY profiles.idLocation
but I still need to have the column giving me the count where User_me.responded = 1
Something like:
SELECT lookup.location, count(*) as total, count(*) responded
If I'm understanding your question correctly, you can you a case statement in the count aggregate:
SELECT lookup.location, count(*) as total,
count(case when user.responded = 1 then 1 end) as responded
FROM User_me user
JOIN Profiles
ON user.profileId= profiles.profileId
JOIN lookup_location lookup
ON profiles.idLocation = lookup.id
GROUP BY profiles.idLocation
Since you're using MySQL, you can also use something like sum(user.responded = 1).

How Can I Correctly Do this Limited Group By Query in MySQL?

I have a query that is returning a list of 25 doctors that needs to reflect their uploaded files (cloud_uploads) in a column. The problem is, I do not want to limit the number of files that are listed, but the limit seems to be applying to the files as well. Other similar questions on SO that I've seen have the opposite problem - they want to limit the files without limiting the total rows returned. I have doctors with more than 25 uploaded files, but the list is only showing the first 25 files rather than all of them.
Because this is a programmatically generated query, I can't separate it into multiple queries. It needs to be one. So, if the answer is that it can't be done, I can accept that.
Here is the current query:
SELECT SQL_CALC_FOUND_ROWS records.instance_id,
instances.patient_id,
TRIM(form_1_data.field_2) AS field_0,
TRIM(form_1_data.field_1) AS field_1,
TRIM(form_1_data.field_87) AS field_2,
GROUP_CONCAT(DISTINCT CONCAT (
extensible_8_data.field_953,
'|||',
cloud_uploads.NAME
) SEPARATOR '<br />') AS field_3
FROM form_6_data AS records
LEFT JOIN instances
ON instances.id = records.instance_id
LEFT JOIN extensible_8_data
ON records.instance_id = extensible_8_data.instance_id
LEFT JOIN cloud_uploads
ON extensible_8_data.field_953 = cloud_uploads.id
LEFT JOIN form_1_data
ON records.field_89 = form_1_data.instance_id
WHERE instances.form_id = 6
AND instances.active = 1
AND (
cloud_uploads.active = 1
OR cloud_uploads.active IS NULL
)
GROUP BY records.instance_id
ORDER BY records.instance_id DESC LIMIT 0, 25
Join a subquery that selects the 25 doctors of interest with their files:
SELECT SQL_CALC_FOUND_ROWS records.instance_id,
instances.patient_id,
TRIM(form_1_data.field_2) AS field_0,
TRIM(form_1_data.field_1) AS field_1,
TRIM(form_1_data.field_87) AS field_2,
GROUP_CONCAT(DISTINCT CONCAT (
extensible_8_data.field_953,
'|||',
cloud_uploads.NAME
) SEPARATOR '<br />') AS field_3
FROM (SELECT * FROM form_6_data ORDER BY instance_id DESC LIMIT 25) AS records
LEFT JOIN instances
ON instances.id = records.instance_id
LEFT JOIN extensible_8_data
ON records.instance_id = extensible_8_data.instance_id
LEFT JOIN cloud_uploads
ON extensible_8_data.field_953 = cloud_uploads.id
LEFT JOIN form_1_data
ON records.field_89 = form_1_data.instance_id
WHERE instances.form_id = 6
AND instances.active = 1
AND (
cloud_uploads.active = 1
OR cloud_uploads.active IS NULL
)
GROUP BY records.instance_id
ORDER BY records.instance_id

MySQL SELECT DISTINCT ORDER BY problem

To begin with I have 4 tables I am dealing with.
I have a classes table that is a 1->N relationship with a sections table which also has a 1->N relationship with a lessons table.
So to put it in perpective:
Classes
Sections
Lessons
The last table is an activityLog, when the student accesses a lesson this is recorded using the following:
ActivityLog Row -> actorID (user ID), classID, sectionID, lessonID
I want to pull out the last 5 unique lessons the student has visited. I tried using both DISTINCT and GROUP BY without success.
The same records are being returned each time, not the latest classes that they have visited.
Using GROUP BY
SELECT activityLog.actorID, activityLog.activityDate,
strClasses.classID, strClasses.className,
strSections.sectionID, strSections.sectionName,
strLessons.lessonID, strLessons.lessonName
FROM activityLog
LEFT JOIN strClasses ON strClasses.classID = activityLog.classID
LEFT JOIN strSections ON strSections.sectionID = activityLog.sectionID
LEFT JOIN strLessons ON strLessons.lessonID = activityLog.lessonID
WHERE activityLog.activityTypeID = 6 AND activityLog.actorID = 3
GROUP BY activityLog.lessonID
ORDER BY activityLog.activityDate DESC
LIMIT 5
Using DISTINCT
SELECT DISTINCT activityLog.actorID,
strClasses.classID, strClasses.className,
strSections.sectionID, strSections.sectionName,
strLessons.lessonID, strLessons.lessonName
FROM activityLog
LEFT JOIN strClasses ON strClasses.classID = activityLog.classID
LEFT JOIN strSections ON strSections.sectionID = activityLog.sectionID
LEFT JOIN strLessons ON strLessons.lessonID = activityLog.lessonID
WHERE activityLog.activityTypeID = 6 AND activityLog.actorID = 3
ORDER BY activityLog.activityDate DESC
LIMIT 5
I cannot figure out why the latest records are not being displayed.
Based on your change, how does this suit you?
SELECT activityLog.actorID, activityLog.activityDate,
strClasses.classID, strClasses.className,
strSections.sectionID, strSections.sectionName,
strLessons.lessonID, strLessons.lessonName
FROM activityLog
LEFT JOIN strClasses ON strClasses.classID = activityLog.classID
LEFT JOIN strSections ON strSections.sectionID = activityLog.sectionID
LEFT JOIN strLessons ON strLessons.lessonID = activityLog.lessonID
WHERE activityLog.activityTypeID = 6 AND activityLog.actorID = 3
AND activityLog.activityDate = (SELECT MAX(activityDate) FROM activityLog AS lookup WHERE lessonID = activityLog.lessonID)
ORDER BY activityLog.activityDate DESC
LIMIT 5
Based on your description, I'm not sure why you're using LEFT JOIN, but I've left it in just in case.
Try group by like below
GROUP BY activityLog.classID,activityLog.sectionID,activityLog.lessonID
I think it will work, or just sent me create scripts for these I will create that query
Well, there's got to be a datetime in the ActivityLog I hope... so Try this:
Select s.Name, c.ClassName
From Students s
left Join On Classes c
On c.ClassId In
(Select Distinct ClassId From Classes
Where (Select Count(Distinct ClassId) From Classes ic
Join ActivityLog l On l.UserId = s.UserId
And l.ClassId = c.ClassId
Where classId = c.ClassId
And activityDateTime > l.activityDateTime)
< 5)